Più valori nel contesto a valore singolo

A causa della gestione degli errori in Go, spesso finisco con più funzioni di valori. Finora, il modo in cui l’ho gestito è stato molto complicato e sto cercando le migliori pratiche per scrivere codice più pulito.

Diciamo che ho la seguente funzione:

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

Come posso assegnare una nuova variabile item.Value . item.Value elegante. Prima di introdurre la gestione degli errori, la mia funzione restituiva l’ item e potevo semplicemente farlo:

 val := Get(1).Value 

Ora faccio questo:

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

Non c’è un modo per accedere direttamente alla prima variabile restituita?

Nel caso di una funzione di ritorno a più valori, non è ansible fare riferimento a campi o metodi di un valore specifico del risultato quando si chiama la funzione.

E se uno di essi è un error , è lì per un motivo (che potrebbe non funzionare) e non dovresti ignorarlo perché se lo fai, il tuo codice successivo potrebbe anche fallire miseramente (ad es. Con conseguente panico di runtime).

Tuttavia potrebbero esserci situazioni in cui si sa che il codice non fallirà in nessuna circostanza. In questi casi è ansible fornire una funzione (o metodo) di supporto che scarterà l’ error (o solleverà un panico di runtime se si verifica ancora).
Questo può essere il caso se fornisci i valori di input per una funzione dal codice e sai che funzionano.
Esempi eccezionali di questo sono i pacchetti template e regexp : se fornisci un modello valido o regexp in fase di compilazione, puoi essere certo che possono essere sempre analizzati senza errori in fase di runtime. Per questo motivo il pacchetto template fornisce la funzione Must(t *Template, err error) *Template e il pacchetto regexp fornisce la funzione MustCompile(str string) *Regexp : non restituiscono error s perché l’uso previsto è dove l’input è garantito che sia valido

Esempi:

 // "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 compiles var validID = regexp.MustCompile(`^[az]+\[[0-9]+\]$`) 

Torna al tuo caso

Se si è certi che Get() non produrrà error per determinati valori di input, è ansible creare una funzione di aiuto Must() che non restituisca l’ error ma sollevi un error runtime se si verifica ancora:

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

Ma non dovresti usare questo in tutti i casi, solo quando sei sicuro che ci riesca. Uso:

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

Alternativa / Semplificazione

Puoi anche semplificarlo ulteriormente se incorpori la chiamata Get() nella tua funzione helper, chiamiamola MustGet :

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

Uso:

 val := MustGet(1).Value 

No, ma questa è una buona cosa dal momento che dovresti sempre gestire i tuoi errori.

Esistono tecniche che è ansible utilizzare per rinviare la gestione degli errori, vedere Gli errori sono valori di 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 } 

In questo esempio dal post del blog illustra come è ansible creare un tipo errWriter che errWriter gestione degli errori fino a quando non si è errWriter chiamare write .

No, non puoi accedere direttamente al primo valore.

Suppongo che un attacco per questo sarebbe quello di restituire una matrice di valori invece di “item” e “err”, e quindi fare solo item, _ := Get(1)[0] ma non lo consiglierei.

Che ne dici di questo modo?

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

Si C’è.

Sorprendente, eh? Puoi ottenere un valore specifico da un ritorno multiplo usando una semplice funzione di 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", aB, aC()) } func E() (bool, string) { return false, "F" } func G() (func()(string), bool) { return func() string { return "Hello" }, true } 

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

Si noti come si seleziona il numero di valore proprio come si farebbe da una slice / array e quindi il tipo per ottenere il valore effettivo.

Puoi leggere di più sulla scienza dietro a questo articolo . Crediti all’autore.