5 回答
TA贡献1863条经验 获得超2个赞
为了详细说明我的评论,Effective Go提到访问映射键的多值分配称为“逗号确定”模式。
有时您需要将缺失的条目与零值区分开来。是否有“UTC”条目,或者是空字符串,因为它根本不在地图中?你可以用多重赋值的形式来区分。
var seconds int
var ok bool
seconds, ok = timeZone[tz]
出于显而易见的原因,这被称为“逗号确定”习语。在这个例子中,如果 tz 存在,秒将被适当地设置并且 ok 为真;如果没有,秒将被设置为零,确定将是假的。
操场证明这个
我们可以看到这与调用常规函数不同,在常规函数中编译器会告诉您出现问题:
package main
import "fmt"
func multiValueReturn() (int, int) {
return 0, 0
}
func main() {
fmt.Println(multiValueReturn)
asgn1, _ := multiValueReturn()
asgn2 := multiValueReturn()
}
在操场上,这将输出
# command-line-arguments
/tmp/sandbox592492597/main.go:14: multiple-value multiValueReturn() in single-value context
这给了我们一个提示,它可能是编译器正在做的事情。搜索“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(其中这个定义确实给我们当的用它在许多提示,但搜索任务源到commaok我们可以看到它时所使用的正从一个信道的值和类型的断言)。记住加粗的部分以备后用!
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()方法。
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()在一定程度上混淆这里作为这将意味着,位置没有相关的文件或行信息与它,但我们会就忽略这个问题。
在该方法中,我们有:
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在除inassignment.go的Checker.initVars()方法之外的任何地方都采用硬编码为 false的参数,我们可以假设您在进行赋值时只会得到两个值,并且在左侧有两个变量。
该unpack方法将通过检查您是否正在索引切片、从通道中获取值或进行类型断言来确定您是否确实获得了ok返回值
由于您只能ok在进行赋值时获取值,因此在您的特定情况下,您将始终需要使用变量
TA贡献1825条经验 获得超6个赞
您可以使用命名返回来保存几个击键:
func FindUserInfo(id string) (i Info, ok bool) {
i, ok = all[id]
return
}
但除此之外,我不认为你想要的是可能的。
TA贡献1796条经验 获得超10个赞
简单地说:你的第二个例子不是有效的 Go 代码的原因是因为语言规范是这样说的。;)
索引映射只会在两个变量的赋值中产生一个次要值。Return 语句不是赋值语句。
在特殊形式的赋值或初始化中使用的类型为 map[K]V 的映射 a 上的索引表达式
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]产生一个额外的无类型布尔值。如果键 x 存在于映射中,则 ok 的值为真,否则为假。
此外,索引映射不是“对多值函数的单一调用”,它是从函数返回值的三种方法之一(第二种方法,其他两种在这里不相关):
有三种方法可以从具有结果类型的函数返回值:
返回值可以在“return”语句中明确列出。每个表达式必须是单值的并且可以分配给函数结果类型的相应元素。
“return”语句中的表达式列表可能是对多值函数的单个调用。效果就好像从该函数返回的每个值都被分配给一个具有相应值类型的临时变量,然后是一个列出这些变量的“返回”语句,此时适用前一种情况的规则。
如果函数的结果类型为其结果参数指定名称,则表达式列表可能为空。结果参数作为普通的局部变量,函数可以根据需要为它们赋值。“return”语句返回这些变量的值。
至于您的实际问题:避免临时变量的唯一方法是使用非临时变量,但这通常是非常不明智的 - 即使在安全的情况下也可能没有太多优化。
那么,为什么语言规范不允许在 return 语句中使用这种特殊的映射索引(或类型断言或通道接收,两者都可以使用“逗号确定”习语)?这是个好问题。我的猜测:保持语言规范简单。
- 5 回答
- 0 关注
- 432 浏览
添加回答
举报