Чтобы подробно рассказать о моем comment, Effective Go упоминает, что назначение нескольких значений при доступе к ключу карты называется шаблоном «запятая».
Иногда вам нужно различать недостающую запись от нулевого значения. Есть ли запись для «UTC» или это пустая строка, потому что ее нет на карте? Вы можете различать форму множественного присвоения.
var seconds int
var ok bool
seconds, ok = timeZone[tz]
По понятным причинам это называется «запятая ОК» идиома. В этом примере, если tz присутствует, секунды будут установлены соответствующим образом, и ok будет истинным; если нет, секунды будут установлены на ноль и ok будет ложным.
Playground demonstrating this
Мы можем видеть, что это отличается от вызова обычной функции, где компилятор скажет вам, что-то не так:
package main
import "fmt"
func multiValueReturn() (int, int) {
return 0, 0
}
func main() {
fmt.Println(multiValueReturn)
asgn1, _ := multiValueReturn()
asgn2 := multiValueReturn()
}
На playground это будет выводить
# command-line-arguments
/tmp/sandbox592492597/main.go:14: multiple-value multiValueReturn() in single-value context
Это дает нам подсказку, что это может быть что-то компилируемое er делает.Searching the source code для «commaOk» дает нам несколько мест, чтобы посмотреть, в том числе types.unpack
На момент написания этого godoc этого метода гласит:
// 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.
//
Ключевые биты этого в том, что этот метод, как представляется, определить, действительно ли «дело с запятой».
Порывшись в этот метод говорит нам, что он будет проверять, если режим операндов индексировать карту или если режим установлен в commaok
(где это is defined дает нам много намеков о том, когда он используется, но поиск источника для присвоений commaok
мы видим, что он используется, когда getting a value from a channel и type assertions). Помните смелый бит позже!
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
является параметром функции. Выяснив, где в этом файле вызывается unpack
, мы видим, что все вызывающие абоненты передают false
в качестве аргумента. Поиск остальной части репозитория приводит нас к assignments.go
в 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())
Так как кажется, что мы можем использовать только «запятая ОК» образец, чтобы получить два возвращаемых значений при выполнении задания на многозначное это походит на правильное место, чтобы посмотреть! В приведенном выше коде проверяется длина левой стороны, а когда unpack
называется параметром allowCommaOk
, это результат l == 2 && !returnPos.IsValid()
. !returnPos.IsValid()
здесь несколько сбивает с толку, поскольку это будет означать, что позиция has no file or line information associated с ним, но мы просто проигнорируем это.
Дальше в этом методе мы имеем:
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
}
Так что же все это говорит нам?
- Поскольку метод
unpack
принимает allowCommaOk
параметра, который жёстко ложь везде кроме в Checker.initVars()
методы assignment.go
«s, вероятно, мы можем предположить, что вы будете только когда-либо получить два значение при выполнении задания и две переменных слева.
- Метод
unpack
будет определить, действительно ли вы на самом деле сделать получить ok
значение в ответ, проверяя, если вы индексировать кусочек, захватывая значение из канала, или делает утверждение типа
- Так как вы можете только получить значение
ok
при выполнении задания он выглядит в вашем конкретном случае, вы всегда должны использовать переменные
Я не являюсь экспертом в том, как Go работает в бэкэнд, но шаблон «comma ok» применяется к встроенным операциям (а не «реальным» функциям). Поскольку это необязательно, похоже, что компилятор проверяет многозначное * присваивание *. Может быть, кто-то [более знакомый с компилятором] (https://github.com/golang/go/blob/254964074f34dc1cb39693f3e23a95938092044f/src/go/types/call.go#L118-L134) может разработать – Lander
, не могли бы вы рассказать о " запятая ok "? ответ был бы оценен (и одобрен), так как я не только пытаюсь сохранить нажатие клавиш, но и понимаю, почему ** это происходит. –
У меня есть сильное подозрение, почему это не разрешено, но мне нужно будет обратиться к спецификации. Я думаю, что для этого есть разумная логическая причина. Довольно уверен, что если бы вы разрешили это, я мог бы придумать некоторые крайние случаи, которые нарушили бы компилятор или имели неопределенное/непоследовательное поведение. – evanmcdonnal