Valeurs multiples dans un contexte à valeur unique
en raison de la manipulation d'erreur dans Go, je finis souvent avec des fonctions de valeurs multiples. Jusqu'à présent, la façon dont j'ai géré cela a été très bordélique et je suis à la recherche de meilleures pratiques pour écrire du code plus propre.
disons que j'ai la fonction suivante:
type Item struct {
Value int
Name string
}
func Get(value int) (Item, error) {
// some code
return item, nil
}
Comment puis-je attribuer une nouvelle variable à item.Value
avec élégance. Avant d'introduire la gestion des erreurs, ma fonction vient de renvoyer 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?
5 réponses
dans le cas d'une fonction de retour multi-valeur, 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'un d'entre eux est un error
, il est là pour une raison (qui est la fonction pourrait fail) et vous devriez Non contourner parce que si vous le faites, votre code suivant pourrait également échouer misérablement (par exemple résultant en exécution de panique).
Toutefois, il pourrait y avoir des situations où vous savoir le code ne manquera pas, dans toutes les circonstances. Dans ces cas, vous pouvez fournir une fonction helper (ou une méthode) qui rejettera le error
(ou suscitera une panique d'exécution si elle se produit encore).
Cela peut être le cas si vous fournissez les valeurs d'entrée pour la fonction de code, et vous savez qu'ils fonctionnent.
Les paquets template
et regexp
en sont de bons exemples: si vous fournissez un modèle valide ou regexp 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 paquet template
fournit la fonction Must(t *Template, err error) *Template
et le paquet regexp
fournit la fonction MustCompile(str string) *Regexp
: ils ne retournent pas error
s parce que leur utilisation prévue est là où l'entrée est garantie d'être valide.
exemples:
// "text" is a valid template, parsing it will not fail
var t = template.Must(template.New("name").Parse("text"))
// `^[a-z]+\[[0-9]+\]$` is a valid regexp, always compiles
var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)
Retour à votre cas
si vous pouvez être certain que Get()
ne produira pas error
pour certaines valeurs d'entrée, vous pouvez créer un helper Must()
fonction qui ne retournerait pas le error
mais soulèverait une panique d'exécution si elle se produit toujours:
func Must(i Item, err error) Item {
if err != nil {
panic(err)
}
return i
}
mais vous ne devez pas utiliser cela dans tous les cas, juste quand vous êtes sûr qu'il réussit. Usage:
val := Must(Get(1)).Value
Alternative / Simplification
vous pouvez même le simplifier davantage si vous incorporez l'appel Get()
dans votre fonction helper, appelons-le MustGet
:
func MustGet(value int) Item {
i, err := Get(value)
if err != nil {
panic(err)
}
return i
}
Utilisation:
val := MustGet(1).Value
voir certains questions intéressantes / connexes:
comment analyser les multiples retours en golang
carte de retour comme "ok" à Golang sur les fonctions normales
Non, mais c'est une bonne chose car vous devriez toujours gérer vos erreurs.
il y a des techniques que vous pouvez employer pour différer la manipulation d'erreur, voir les erreurs sont des valeurs par 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 du billet de blog, il illustre comment vous pouvez créer un type errWriter
qui reporte la gestion des erreurs jusqu'à ce que vous ayez fini d'appeler write
.
Non, vous ne pouvez pas accéder directement à la première valeur.
je suppose qu'un hack pour cela serait de retourner un tableau de valeurs au lieu de" item "et" err", et puis juste faire
item, _ := Get(1)[0]
mais je ne recommanderais pas cela.
par ici?
package main
import (
"fmt"
"errors"
)
type Item struct {
Value int
Name string
}
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 en a.
surprenant, hein? Vous pouvez obtenir une valeur spécifique à partir d'une déclaration multiple en utilisant une simple fonction mute
:
package main
import "fmt"
import "strings"
func µ(a ...interface{}) []interface{} {
return a
}
type A struct {
B string
C func()(string)
}
func main() {
a := A {
B:strings.TrimSpace(µ(E())[1].(string)),
C:µ(G())[0].(func()(string)),
}
fmt.Printf ("%s says %s\n", a.B, a.C())
}
func E() (bool, string) {
return false, "F"
}
func G() (func()(string), bool) {
return func() string { return "Hello" }, true
}
https://play.golang.org/p/IwqmoKwVm -
remarquez comment vous sélectionnez le nombre de valeur juste comme vous le feriez à partir d'une tranche/tableau et ensuite le type pour obtenir la valeur réelle.
vous pouvez en savoir plus sur la science derrière cela de cet article . Les crédits à l'auteur.