为什么 go panic recover to return value with local variable 不起作用?

Why does go panic recover to return value with local variable not work?

panic recover code 与命名的 return 值一起使用。

func main() {
    result, err := foo()
    fmt.Println("result:", result)
    if err != nil {
        fmt.Println("err:", err)
    }
}

func foo() (result int, err error) {
    defer func() {
        if e := recover(); e != nil {
            result = -1
            err = errors.New(e.(string))
        }
    }()
    bar()

    result = 100
    err = nil
    return
}

func bar() {
    panic("panic happened")
}

输出

result: -1
err: panic happened

但是为什么 this code 使用局部变量不起作用?

func main() {
    result, err := foo()
    fmt.Println("result:", result)
    if err != nil {
        fmt.Println("err:", err)
    }
}

func foo() (int, error) {
    var result int
    var err error
    defer func() {
        if e := recover(); e != nil {
            result = -1
            err = errors.New(e.(string))
        }
    }()
    bar()

    result = 100
    err = nil
    return result, err
}

func bar() {
    panic("panic happened")
}

输出

result: 0

有什么解释可以帮助我理解它的原因/基本概念吗? go tour basics中解释如下

Named return values Go's return values may be named. If so, they are treated as variables defined at the top of the function.

所以应该是一样的吧?

请注意,这与panic/recover无关,它是defer语句的一个特征。

... if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned. If the deferred function has any return values, they are discarded when the function completes.

Spec: Return statements 详细说明:

There are three ways to return values from a function with a result type:

  1. The return value or values may be explicitly listed in the "return" statement. Each expression must be single-valued and assignable to the corresponding element of the function's result type.
  2. The expression list in the "return" statement may be a single call to a multi-valued function. The effect is as if each value returned from that function were assigned to a temporary variable with the type of the respective value, followed by a "return" statement listing these variables, at which point the rules of the previous case apply.
  3. The expression list may be empty if the function's result type specifies names for its result parameters. The result parameters act as ordinary local variables and the function may assign values to them as necessary. The "return" statement returns the values of these variables.

所以基本上,如果您使用明确列出 return 值的 return 语句,将使用这些值,无论结果参数是否命名。

如果结果参数被命名,它们就像普通的局部变量一样:你可以读写它们。 如果结果参数已命名,您可以使用“裸”return 语句,而不列出 return 的值。如果这样做,那么实际的 return 值将是(命名的)结果参数的值。 如果您的函数由于恐慌和恢复而没有达到 return 语句,则同样适用:一旦延迟函数 运行,实际的 return 值将是值命名结果参数的(延迟函数可以更改并“有发言权”return)。

如果你不使用命名结果参数但你声明局部变量,它们在这方面并不特殊:当函数 returns,它们不会“自动”用作结果值(就像它们被命名为结果参数而不是局部变量一样)。因此,如果您在延迟函数中更改它们,那将不会对实际值产生任何影响 returned。事实上,如果你不使用命名结果参数并且你的函数崩溃并恢复,你不能指定 return 值,它们将是结果类型的 zero values 。这就是为什么您看到 result: 00int 的零值)并且没有错误(因为 error 是接口类型,接口类型的零值是 nil 如果是 nil).

则不会打印错误

查看相关:

可能是对@icza 的回答的简短总结:

  1. 命名的 return 变量在函数终止时使用它们的最终值 returning 没有恐慌(return 通常或从恐慌中恢复),因此您可以延迟更改它们恢复 func(),最终值发生了变化,因此成为 return 值。
  2. 如果使用局部变量,编译器无法知道这些局部变量会被用作return变量,直到正常return。局部变量可能会在 panic recover 中改变,但是 由于 panic,return 语句尚未执行,因此您定义的局部变量未被视为 return 变量,return 值将是 [=18 的零值=] 类型。