Per elaborare il mio comment, il Effective Go indica che l'assegnazione a più valori dall'accesso a una chiave di mappa è denominata "virgola ok".
A volte è necessario distinguere una voce mancante da un valore zero. C'è una voce per "UTC" o è la stringa vuota perché non è nella mappa? Puoi discriminare con una forma di assegnazione multipla.
var seconds int
var ok bool
seconds, ok = timeZone[tz]
Per ovvie ragioni questo è chiamato il “comma ok” idioma. In questo esempio, se tz è presente, i secondi saranno impostati in modo appropriato e ok sarà vero; in caso contrario, i secondi saranno impostati su zero e ok sarà falso.
Playground demonstrating this
Possiamo vedere che questo si differenzia da chiamare una funzione regolare in cui il compilatore vi direbbe che qualcosa non va:
package main
import "fmt"
func multiValueReturn() (int, int) {
return 0, 0
}
func main() {
fmt.Println(multiValueReturn)
asgn1, _ := multiValueReturn()
asgn2 := multiValueReturn()
}
Sulla playground questa uscita volontà
# command-line-arguments
/tmp/sandbox592492597/main.go:14: multiple-value multiValueReturn() in single-value context
Questo ci dà un suggerimento che potrebbe essere qualcosa la compilazione sta facendo.Searching the source code per "commaOk" ci dà un paio di posti per cercare, tra cui types.unpack
Al momento della stesura di questo GODOC questo il metodo legge:
// unpack takes a getter get and a number of operands n. If n == 1, unpack
// calls the incoming getter for the first operand. If that operand is
// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
// function call, or a comma-ok expression and allowCommaOk is set, the result
// is a new getter and operand count providing access to the function results,
// or comma-ok values, respectively. The third result value reports if it
// is indeed the comma-ok case. In all other cases, the incoming getter and
// operand count are returned unchanged, and the third result value is false.
//
// In other words, if there's exactly one operand that - after type-checking
// by calling get - stands for multiple operands, the resulting getter provides
// access to those operands instead.
//
// If the returned getter is called at most once for a given operand index i
// (including i == 0), that operand is guaranteed to cause only one call of
// the incoming getter with that i.
//
I bit chiave di questo è che questo metodo sembra determinare se qualcosa è effettivamente un caso "virgola ok".
Scavando in quel metodo ci dice che controllerà per vedere se la modalità degli operandi è indicizzazione una mappa o se la modalità è impostata su commaok
(dove questa is defined ci dà molti suggerimenti su quando viene utilizzato, ma cercando la fonte per i compiti a commaok
possiamo vedere che è usato quando getting a value from a channel e type assertions). Ricorda il pezzo audace per dopo!
if x0.mode == mapindex || x0.mode == commaok {
// comma-ok value
if allowCommaOk {
a := [2]Type{x0.typ, Typ[UntypedBool]}
return func(x *operand, i int) {
x.mode = value
x.expr = x0.expr
x.typ = a[i]
}, 2, true
}
x0.mode = value
}
allowCommaOk
è un parametro alla funzione. Controllando dove viene chiamato unpack
in quel file possiamo vedere che tutti i chiamanti passano come argomento false
. La ricerca nel resto del repository ci porta a assignments.go
nello Checker.initVars()
method.
l := len(lhs)
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
Poiché sembra che siamo in grado di utilizzare solo il modello "comma ok" per ottenere due valori di ritorno quando si fa un'assegnazione più valori questo mi sembra il posto giusto per cercare! Nel codice sopra riportato viene verificata la lunghezza della parte sinistra e, quando viene chiamato il parametro unpack
, il parametro allowCommaOk
è il risultato di l == 2 && !returnPos.IsValid()
. Il !returnPos.IsValid()
è un po 'di confusione in quanto ciò significherebbe che la posizione has no file or line information associated con esso, ma lo ignoreremo.
Più in basso in questo metodo abbiamo:
var x operand
if commaOk {
var a [2]Type
for i := range a {
get(&x, i)
a[i] = check.initVar(lhs[i], &x, returnPos.IsValid())
}
check.recordCommaOkTypes(rhs[0], a)
return
}
Che cosa significa tutto questo ci dice?
- Poiché il metodo
unpack
accetta un parametro allowCommaOk
che è codificato a false ovunque tranne in Checker.initVars()
metodo s' assignment.go
, probabilmente possiamo supporre che si sempre e solo ottenere due valori quando si fa un compito e hanno due variabili sul lato sinistro.
- Il metodo
unpack
sarà determinare se effettivamente fai ottenere un valore ok
in cambio controllando se si è l'indicizzazione di un fetta, afferrando un valore da un canale, o fare un tipo di affermazione
- Dal momento che si può solo ottenere il valore
ok
quando facendo un incarico sembra che nel tuo caso specifico si avrà sempre bisogno di usare le variabili
Non sono esperto in come Go funziona nel back-end, ma il modello "virgola ok" viene applicato alle operazioni incorporate (non alle funzioni "reali"). Dal momento che è opzionale, sembrerebbe che il compilatore verifichi l'assegnazione * a più valori *. Forse qualcuno [più familiare con il compilatore] (https://github.com/golang/go/blob/254964074f34dc1cb39693f3e23a95938092044f/src/go/types/call.go#L118-L134) può elaborare – Lander
ti dispiacerebbe elaborare sul " virgola ok "modello? una risposta sarebbe apprezzata (e svalutata) dal momento che non sto solo cercando di salvare i tasti ma comprendendo ** perché ** questo accade. –
Ho un forte sospetto sul motivo per cui ciò non è consentito, ma dovrò fare riferimento alle specifiche. Penso che ci sia un motivo logico valido per questo. Abbastanza sicuro che se lo permetteste potrei inventare alcuni casi limite che interromperanno il compilatore o comportano un comportamento indefinito/incoerente. – evanmcdonnal