Function golang返回多值问题

Function golang返回多值问题,function,go,Function,Go,我想知道为什么这是有效的go代码: func FindUserInfo(id string) (Info, bool) { it, present := all[id] return it, present } 但事实并非如此 func FindUserInfo(id string) (Info, bool) { return all[id] } 有没有避免临时变量的方法?您可以使用命名返回保存两个按键: func FindUserInfo(id string) (i

我想知道为什么这是有效的go代码:

func FindUserInfo(id string) (Info, bool) {
    it, present := all[id]
    return it, present
}
但事实并非如此

func FindUserInfo(id string) (Info, bool) {
    return all[id]
}

有没有避免临时变量的方法?

您可以使用命名返回保存两个按键:

func FindUserInfo(id string) (i Info, ok bool) {
    i, ok = all[id]
    return
}

但是除此之外,我不认为你想要什么是可能的。

我不是专家,但我相信当你试图返回数组时,你会遇到编译时错误,即
返回所有[id]
。原因可能是函数返回类型被特别提到为
(Info,bool)
,当您执行
返回所有[id]
时,它无法将
所有[id]
的返回类型映射到
(Info,bool)


然而,在上述解决方案中,返回的变量
i
ok
与函数
(i Info,ok bool)
的返回类型中提到的变量相同,因此编译器知道它返回的是什么,而不是默认情况下只执行
(i Info,ok bool)
,golang中的贴图在访问键时返回单个值

因此,
return all[id]
不会为一个需要2个返回值的函数编译。

为了详细说明my,提到访问映射键的多值赋值被称为“逗号ok”模式

有时,您需要区分缺少的条目和零值。是否有“UTC”的条目,或者该条目是空字符串,因为它根本不在地图中?你可以用多重赋值的形式来区分

出于显而易见的原因,这被称为“逗号ok”成语。在本例中,如果tz存在,秒将被适当设置,ok将为真;否则,秒将设置为零,ok将为false

我们可以看出,这与调用常规函数不同,在常规函数中,编译器会告诉您有问题:

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”给了我们一些可以看的地方,包括

在编写本文时,该方法的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.
//
关键的一点是,这个方法似乎可以确定某个东西是否是“逗号ok”的情况

深入研究该方法告诉我们,它将检查操作数的模式是否是索引映射的或模式是否设置为
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
作为参数传递。搜索存储库的其余部分将导致我们找到
工作分配。转到中的

因为在执行多值赋值时,我们似乎只能使用“逗号ok”模式来获取两个返回值,所以这似乎是一个正确的地方!在上述代码中,检查左侧的长度,当调用
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
    参数,除了在
    assignment.go
    Checker.initVars()
    方法中,该参数在任何地方都被硬编码为false,因此我们可以假设在执行赋值时只会得到两个值,并且在左侧有两个变量
  • unpack
    方法将通过检查您是否正在索引切片、从通道获取值或执行类型断言来确定您是否确实得到了
    ok
    值作为回报
  • 由于在执行赋值时只能获得
    ok
    值,因此在您的特定情况下,似乎始终需要使用变量

简单地说:第二个示例之所以不是有效的Go代码,是因为语言规范这么说的

在两个变量的赋值中只产生一个次值。Return语句不是赋值

map[K]V类型映射上的索引表达式,用于特殊形式的赋值或初始化

v、 ok=a[x]
v、 确定:=a[x]
var v,ok=a[x]

生成一个额外的非类型化布尔值。如果映射中存在键x,则ok的值为true,否则为false

此外,为映射编制索引不是一个“”,这是从函数返回值的三种方法之一(第二种方法,其他两种方法在此不相关):

有三种方法可以从结果类型的函数返回值:

  • 返回值可以在“return”语句中明确列出。每个表达式必须是单值的,并且可以赋值给函数结果类型的相应元素

  • “return”语句中的表达式列表可以是对多值函数的单个调用。其效果就好像从该函数返回的每个值都被分配给了一个临时变量,该临时变量具有相应的值的类型,然后是一个列出这些变量的“return”语句,在这一点上,前面案例的规则适用

  • 如果函数的结果类型为其结果参数指定名称,则表达式列表可能为空。结果参数充当普通局部变量,函数可以根据需要为其赋值。“return”语句返回这些变量的值

  • 至于你的实际问题:避免临时变量的唯一方法是使用
    l := len(lhs)
    get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !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
    }