Plusieurs valeurs dans un contexte à valeur unique

En raison de la gestion des erreurs dans Go, je me retrouve souvent avec plusieurs fonctions de valeurs. Jusqu’à présent, la façon dont j’ai géré cela a été très compliquée et je recherche les meilleures pratiques pour écrire du code plus propre.

Disons que j’ai la fonction suivante:

type Item struct { Value int Name ssortingng } func Get(value int) (Item, error) { // some code return item, nil } 

Comment atsortingbuer une nouvelle variable à item.Value élégamment. Avant d’introduire la gestion des erreurs, ma fonction vient de renvoyer l’ item et je pourrais simplement faire ceci:

 val := Get(1).Value 

Maintenant je fais ceci:

 item, _ := Get(1) val := item.Value 

N’y a-t-il pas un moyen d’accéder directement à la première variable renvoyée?

Dans le cas d’une fonction de retour à plusieurs valeurs, vous ne pouvez pas vous référer aux champs ou aux méthodes d’une valeur spécifique du résultat lors de l’appel de la fonction.

Et si l’une d’elles est une error , elle existe pour une raison (la fonction peut échouer) et vous ne devez pas l’ ignorer car si vous le faites, votre code ultérieur peut également échouer lamentablement (par exemple, ce qui entraîne une panique à l’exécution).

Cependant, il peut y avoir des situations où vous savez que le code n’échouera en aucune circonstance. Dans ces cas, vous pouvez fournir une fonction d’ assistance (ou une méthode) qui éliminera l’ error (ou déclenchera une panique d’exécution si elle se produit toujours).
Cela peut être le cas si vous fournissez les valeurs d’entrée d’une fonction à partir du code et que vous savez qu’elles fonctionnent.
Un bon exemple en est le template et les paquets regexp : si vous fournissez un template ou une regexp valide au moment de la compilation, vous pouvez être sûr qu’ils peuvent toujours être analysés sans erreur au moment de l’exécution. Pour cette raison, le package de template fournit la fonction Must(t *Template, err error) *Template et le package regexp fournit la fonction MustComstack(str ssortingng) *Regexp : ils ne renvoient pas d’ error s car leur utilisation prévue est celle où l’entrée est garanti pour être valide.

Exemples:

 // "text" is a valid template, parsing it will not fail var t = template.Must(template.New("name").Parse("text")) // `^[az]+\[[0-9]+\]$` is a valid regexp, always comstacks var validID = regexp.MustComstack(`^[az]+\[[0-9]+\]$`) 

Retour à votre cas

Si vous pouvez être certain que Get() ne produira pas d’ error pour certaines valeurs d’entrée, vous pouvez créer une fonction Must() assistance qui ne renverrait pas l’ error mais déclencherait une panique d’exécution si elle se produit encore:

 func Must(i Item, err error) Item { if err != nil { panic(err) } return i } 

Mais vous ne devriez pas utiliser cela dans tous les cas, juste au moment où vous êtes sûr que cela réussit. Usage:

 val := Must(Get(1)).Value 

Alternative / Simplification

Vous pouvez même le simplifier davantage si vous MustGet l’appel Get() dans votre fonction d’assistance, appelons-le MustGet :

 func MustGet(value int) Item { i, err := Get(value) if err != nil { panic(err) } return i } 

Usage:

 val := MustGet(1).Value 

Non, mais c’est une bonne chose puisque vous devez toujours gérer vos erreurs.

Il existe des techniques que vous pouvez utiliser pour différer la gestion des erreurs, voir Les erreurs sont des valeurs de Rob Pike.

 ew := &errWriter{w: fd} ew.write(p0[a:b]) ew.write(p1[c:d]) ew.write(p2[e:f]) // and so on if ew.err != nil { return ew.err } 

Dans cet exemple de l’article de blog, il illustre comment vous pouvez créer un type errWriter qui diffère le traitement des erreurs jusqu’à ce que vous ayez fini d’ write .

Non, vous ne pouvez pas accéder directement à la première valeur.

Je suppose qu’un hack pour cela serait de renvoyer un tableau de valeurs au lieu de “item” et “err”, puis de simplement faire un item, _ := Get(1)[0] mais je ne le recommanderais pas.

Que diriez-vous de cette façon?

 package main import ( "fmt" "errors" ) type Item struct { Value int Name ssortingng } var items []Item = []Item{{Value:0, Name:"zero"}, {Value:1, Name:"one"}, {Value:2, Name:"two"}} func main() { var err error v := Get(3, &err).Value if err != nil { fmt.Println(err) return } fmt.Println(v) } func Get(value int, err *error) Item { if value > (len(items) - 1) { *err = errors.New("error") return Item{} } else { return items[value] } } 

Oui il y a.

Surprenant, hein? Vous pouvez obtenir une valeur spécifique à partir d’un retour multiple en utilisant une simple fonction de mise en mute :

 package main import "fmt" import "ssortingngs" func µ(a ...interface{}) []interface{} { return a } type A struct { B ssortingng C func()(ssortingng) } func main() { a := A { B:ssortingngs.TrimSpace(µ(E())[1].(ssortingng)), C:µ(G())[0].(func()(ssortingng)), } fmt.Printf ("%s says %s\n", aB, aC()) } func E() (bool, ssortingng) { return false, "F" } func G() (func()(ssortingng), bool) { return func() ssortingng { return "Hello" }, true } 

https://play.golang.org/p/IwqmoKwVm-

Remarquez comment vous sélectionnez le numéro de valeur comme vous le feriez pour une tranche / un tableau, puis le type pour obtenir la valeur réelle.

Vous pouvez en savoir plus sur la science derrière cet article . Crédits à l’auteur.