Comment définir les valeurs par défaut dans les structures Go

143

Il existe plusieurs réponses / techniques à la question ci-dessous:

  1. Comment définir les valeurs par défaut des structures golang?
  2. Comment initialiser des structures dans Golang

J'ai quelques réponses, mais une discussion plus approfondie est nécessaire.

Prateek
la source
@icza Votre réponse fournit un moyen de le faire, mais en passant par le titre de la question, ce n'est en aucun cas similaire ou consultable car il s'agit d'une question très spécifique. J'ajouterai cependant le lien dans ma réponse.
Prateek
Il y a deux questions ici, choisissez-en une. En supposant que vous optez pour la première question (selon le titre de la question), veuillez être plus précis sur vos recherches antérieures et sur les domaines où vos autres réponses nécessitent plus de discussion.
Duncan Jones

Réponses:

96

Une idée possible est d'écrire une fonction constructeur distincte

//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
la source
6
Oui, c'est l'un des moyens que j'ai également mentionnés dans ma réponse, mais il n'y a aucun moyen de forcer quiconque à utiliser uniquement cette fonction.
Prateek
@Prateek, c'est soit ceci, soit utiliser une interface, qui serait moche et trop compliquée.
OneOfOne
31
@Prateek oui, vous pouvez forcer les gens à utiliser ce constructeur si vous rendez simplement le type lui-même non exporté. Vous pouvez exporter la fonction NewSomethinget même les champs Textet DefaultText, mais ne pas exporter le type de structure something.
Amit Kumar Gupta
1
Le problème est pire ... si un tiers (bibliothèque, par exemple) est utilisé pour instancier votre structure (via reflect.New(), par exemple), il ne peut pas s'attendre à connaître votre fonction de fabrique spécialement nommée. Dans ce cas, et à moins que la langue elle-même ne soit modifiée, seule une interface (que la bibliothèque pourrait vérifier) ​​ferait l'affaire, je pense.
edam
1
Il est bon de définir la valeur par défaut, mais parfois, je pourrais vouloir remplacer la valeur par défaut. Dans ce cas, je ne pourrai pas initialiser une structure avec une valeur qui n'est pas la valeur par défaut. un peu ennuyeux pour moi
Juliatzin
68
  1. Forcer une méthode à obtenir la structure (la manière du constructeur).

    Une bonne conception consiste à rendre votre type non exporté, mais à fournir une fonction de constructeur exportée comme NewMyType () dans laquelle vous pouvez correctement initialiser votre structure / type. Renvoyez également un type d'interface et non un type concret, et l'interface doit contenir tout ce que les autres veulent faire avec votre valeur. Et votre type concret doit implémenter cette interface bien sûr.

    Cela peut être fait en rendant simplement le type lui-même non exporté. Vous pouvez exporter la fonction NewSomething et même les champs Text et DefaultText, mais ne pas exporter le type de structure quelque chose

  2. Une autre façon de le personnaliser pour votre propre module consiste à utiliser une structure de configuration pour définir les valeurs par défaut (option 5 dans le lien). Ce n'est pas un bon moyen cependant.

Prateek
la source
7
C'est maintenant un lien cassé (404): joneisen.tumblr.com/post/53695478114/golang-and-default-values
Victor Zamanian
3
Il est disponible dans la machine de retour .
n8henrie
FWIW, je pense que c'est «Option 3» - au moins dans le lien de la machine de retour. (Il n'y a pas d'`` option 5 '', ici).
decimus phostle
@ m90 pour faire taire Golint, vous pouvez déclarer votre fonction comme renvoyant le type d'interface publique
Thomas Grainger
@ThomasGrainger Mon commentaire semble faire référence à une révision précédente de cette réponse, cela n'a plus vraiment de sens comme ça :) Je vais simplement la supprimer.
m90
32

Un problème avec l'option 1 dans la réponse de Victor Zamanian est que si le type n'est pas exporté, les utilisateurs de votre package ne peuvent pas le déclarer comme type pour les paramètres de fonction, etc. Une solution serait d'exporter une interface au lieu 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
}

Ce qui nous permet de déclarer les types de paramètres de fonction à l'aide de l'interface Candidate exportée. Le seul inconvénient que je peux voir avec cette solution est que toutes nos méthodes doivent être déclarées dans la définition de l'interface, mais vous pouvez dire que c'est de toute façon une bonne pratique.

wolfson109
la source
est disponible pour changer la variable Nom et Votes après l'appel Nouvelle fonction?
morteza khadem
Bel exemple simple.
petite faute de frappe: Votes unit32devrait probablement êtreVotes uint32
PartyLich
@PartyLich bien repéré. Devrait être corrigé.
wolfson109
13

Il existe un moyen de faire cela avec des balises, qui permet plusieurs valeurs par défaut.

Supposons que vous ayez la structure suivante, avec 2 balises par défaut default0 et default1 .

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

Il est maintenant possible de définir les valeurs par défaut.

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...
}

Voici le programme complet dans une aire de jeux .

Si vous êtes intéressé par un exemple plus complexe, par exemple avec des tranches et des cartes, jetez un œil à creasty / defaultse

Mike Chirico
la source
Merci beaucoup! J'ai commencé à écrire le même code que la bibliothèque suggéré et suis tombé sur ce post. Il fait exactement ce que vous attendez ( github.com/creasty/defaults ). Si vous n'avez pas de valeur, il définit la valeur par défaut, mais si vous avez attribué une valeur à votre variable, elle n'affectera pas la valeur par défaut. Cela fonctionne plutôt bien avec la bibliothèque yaml.v2.
Nordes
3

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

Parfois, la valeur zéro n'est pas assez bonne et un constructeur d'initialisation est nécessaire, comme dans cet exemple dérivé du package 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
la source
-3
type Config struct {
    AWSRegion                               string `default:"us-west-2"`
}
user10209901
la source
1
Ceci est une erreur. Au mieux, vous pouvez définir une valeur de balise sur ce champ, puis atteindre sa valeur avec réflexion, mais même avec cela, la syntaxe est incorrecte (il manque des coches arrière) et vous ne pourrez définir qu'une valeur par défaut pour un type de chaîne. Si vous avez un aperçu de ce à quoi cet exemple fait référence, veuillez ajouter un lien vers lequel vous référer.
markeissler