Cómo establecer valores predeterminados en estructuras Go

143

Existen múltiples respuestas / técnicas para la siguiente pregunta:

  1. ¿Cómo establecer valores predeterminados para estructuras de golang?
  2. Cómo inicializar estructuras en golang

Tengo un par de respuestas pero se requiere más discusión.

Prateek
fuente
@icza Su respuesta proporciona una manera de hacerlo, pero siguiendo el Título de la pregunta, no es similar ni se puede buscar ya que es una pregunta muy específica. Sin embargo, agregaré el enlace en mi respuesta.
Prateek
Aquí hay dos preguntas, elige una. Suponiendo que opta por la primera pregunta (según el título de la pregunta), sea más específico sobre su investigación previa y sobre dónde sus otras respuestas requieren más discusión.
Duncan Jones

Respuestas:

96

Una idea posible es escribir una función de constructor separada

//Something is the structure we work with
type Something struct {
     Text string 
     DefaultText string 
} 
// NewSomething create new instance of Something
func NewSomething(text string) Something {
   something := Something{}
   something.Text = text
   something.DefaultText = "default text"
   return something
}
vodolaz095
fuente
66
Sí, esta es una de las formas en que también he mencionado en mi respuesta, pero no hay forma de que podamos obligar a alguien a usar esta función únicamente.
Prateek
@Prateek es esto o usar una interfaz, que sería fea y demasiado complicada.
OneOfOne
31
@Prateek sí, puede obligar a las personas a usar este constructor si simplemente hace que el tipo en sí no se exporte. Puede exportar la función NewSomethinge incluso los campos Texty DefaultText, pero simplemente no exportar el tipo de estructura something.
Amit Kumar Gupta
1
El problema es peor ... si se usa un tercero (biblioteca, por ejemplo) para crear una instancia de su estructura (a través de reflect.New(), por ejemplo), no se puede esperar que sepa sobre su función de fábrica especialmente nombrada. En ese caso, y salvo que se cambie el idioma en sí , creo que solo una interfaz (que la biblioteca podría verificar) funcionaría.
Edam
1
Es bueno establecer el valor predeterminado, pero a veces, es posible que desee anular el valor predeterminado. En este caso, no podré inicializar una estructura con un valor que no sea el predeterminado. un poco molesto para mí
Juliatzin
68
  1. Forzar un método para obtener la estructura (la forma del constructor).

    Un buen diseño es hacer que su tipo no se exporte, pero proporcionar una función de constructor exportada como NewMyType () en la que pueda inicializar correctamente su estructura / tipo. También devuelva un tipo de interfaz y no un tipo concreto, y la interfaz debe contener todo lo que otros quieran hacer con su valor. Y su tipo concreto debe implementar esa interfaz, por supuesto.

    Esto se puede hacer simplemente haciendo que el tipo en sí no se exporte. Puede exportar la función NewSomething e incluso los campos Text y DefaultText, pero simplemente no exporte el tipo de estructura algo

  2. Otra forma de personalizarlo para su propio módulo es mediante el uso de una estructura de configuración para establecer los valores predeterminados (opción 5 en el enlace). Sin embargo, no es una buena manera.

Prateek
fuente
77
Este es ahora un enlace roto (404): joneisen.tumblr.com/post/53695478114/golang-and-default-values
Victor Zamanian
3
Está disponible en la máquina wayback .
n8henrie
FWIW, creo que es la 'Opción 3', al menos en el enlace de la máquina wayback. (No hay 'Opción 5', allí).
decimus phostle
@ m90 para silenciar golint, puede declarar que su función devuelve el tipo de interfaz pública
Thomas Grainger
@ThomasGrainger Mi comentario parece referirse a una revisión anterior de esta respuesta, ya no tiene ningún sentido como este :) Simplemente lo eliminaré.
m90
32

Un problema con la opción 1 en respuesta de Victor Zamanian es que si el tipo no se exporta, los usuarios de su paquete no pueden declararlo como el tipo para los parámetros de función, etc. Una solución sería exportar una interfaz en lugar de struct eg

package candidate
// Exporting interface instead of struct
type Candidate interface {}
// Struct is not exported
type candidate struct {
    Name string
    Votes uint32 // Defaults to 0
}
// We are forced to call the constructor to get an instance of candidate
func New(name string) Candidate {
    return candidate{name, 0}  // enforce the default value here
}

Lo que nos permite declarar los tipos de parámetros de función utilizando la interfaz Candidate exportada. La única desventaja que puedo ver de esta solución es que todos nuestros métodos deben declararse en la definición de la interfaz, pero podría argumentar que es una buena práctica de todos modos.

wolfson109
fuente
está disponible para cambiar la variable Nombre y Votos después de llamar ¿Nueva función?
morteza khadem
Buen ejemplo simple.
pequeño error tipográfico: Votes unit32probablemente debería serVotes uint32
PartyLich
@PartyLich bien visto. Debería ser arreglado.
wolfson109
13

Hay una manera de hacerlo con etiquetas, que permite múltiples valores predeterminados.

Suponga que tiene la siguiente estructura, con 2 etiquetas predeterminadas default0 y default1 .

type A struct {
   I int    `default0:"3" default1:"42"`
   S string `default0:"Some String..." default1:"Some Other String..."`
}

Ahora es posible establecer los valores predeterminados.

func main() {

ptr := &A{}

Set(ptr, "default0")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=3 ptr.S=Some String...

Set(ptr, "default1")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=42 ptr.S=Some Other String...
}

Aquí está el programa completo en un patio de recreo. .

Si está interesado en un ejemplo más complejo, digamos con cortes y mapas, luego, eche un vistazo a creasty / defaultse

Mike Chirico
fuente
¡Muchas gracias! Comencé a escribir el mismo código que sugirió la biblioteca y me encontré con esta publicación. Hace exactamente lo que espera ( github.com/creasty/defaults ). Si no tiene ningún valor, establece el valor predeterminado, pero si asignó un valor a su variable, entonces no asignará el valor predeterminado. Funciona bastante bien con la biblioteca yaml.v2.
Nordes
3

De https://golang.org/doc/effective_go.html#composite_literals :

A veces el valor cero no es lo suficientemente bueno y es necesario un constructor de inicialización, como en este ejemplo derivado del paquete os.

    func NewFile(fd int, name string) *File {
      if fd < 0 {
        return nil
      }
      f := new(File)
      f.fd = fd
      f.name = name
      f.dirinfo = nil
      f.nepipe = 0
      return f
}
RdB
fuente
-3
type Config struct {
    AWSRegion                               string `default:"us-west-2"`
}
usuario10209901
fuente
1
Esto es incorrecto. En el mejor de los casos, podría establecer un valor de etiqueta en ese campo y luego llegar a su valor con reflexión, pero incluso con esto la sintaxis es incorrecta (faltan ticks) y solo podrá establecer un valor predeterminado para un tipo de cadena. Si tiene alguna idea de a qué se refiere este ejemplo específicamente, agregue un enlace para consultarlo.
markeissler