命名返回值和正常返回值之间有什么不同?

时间:2018-02-08 07:50:13

标签: go

关于不同的命名返回值与正常返回值的问题

我的代码

package main

import "fmt"

func main() {
    f := fmt.Println
    f(a())
    f(b())
}

func a() int {
    i := 0
    defer func() {
        i += 1
        fmt.Println("a defer : ", i)
    }()

    return i
}

func b() (i int) {
    i = 0

    defer func() {
        i += 1
        fmt.Println("b defer : ", i)
    }()
    return i
}

结果:

a函数返回0

b函数reutrn 1

为什么?

3 个答案:

答案 0 :(得分:5)

命名返回值还为函数范围分配变量。

func a() int:虽然您已经返回i = 0的值,但由于未定义任何命名值,因此返回了静态值。因此,即使您在延迟函数中增加i,它也不会影响返回的值。

func b() (i int):已分配变量i(已初始化为0)。即使延迟函数在返回i = 0之后运行,范围仍然可访问,因此仍然可以更改。

另一种观点:您仍然可以在延迟函数中更改命名返回值,但不能更改常规返回值。

在以下示例中尤其如此:

func c() (i int) {
    defer func() {
        i = 1
        fmt.Println("c defer : ", i)
    }()
    return 0
}

答案 1 :(得分:1)

deferreturn语句之后但在函数返回之前运行一个函数,从而启用修改返回的结果。但是,只能通过变量名称正常访问命名的返回结果。

return语句,当不是赤裸裸的时候(关于命名返回的另一件事,但在这里不相关),表达式得到了评估。如果命名了返回,则为命名变量分配评估值。

在您的代码中,在func a() int中输入的内容但未命名。因此,当return i被执行时,它会将返回值(代码不可用的变量)设置为i的值。您可以将其视为RETVAL := i。之后,您的延迟函数已修改i,但返回值(RETVAL)保持不变。

但是在func b() (i int)中,我是一个命名的回归。因此,当return i执行时,它会逐字转换为i = i。之后,您的deffered函数修改了i,返回值,因此返回的值会更改。

有关return的更多信息:https://golang.org/ref/spec#Return_statements

答案 2 :(得分:0)

使用命名的返回值,您可以直接修改返回的内容,使用“normal”返回值只需修改函数范围内的局部变量,该变量永远不会被返回。

更多相关内容:

延迟函数可以访问命名的返回值,但它本身没有返回值 - 所以这实际上是从那里修改主函数结果的唯一方法。非常有用的东西。

想象一下,您想要修复恐慌的代码 - 您希望它返回错误。您可以在延迟函数中使用recover然后将已恢复的错误分配给命名的返回值来解决此问题。

示例,有点抽象,但希望有用:

func noMorePanics() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = r.(error)
        }
    }()

    potentiallyPanickingFunction()
}