为什么 defer 对周围函数中定义的变量的行为与命名结果不同?

the*_*hai 3 go

下面的程序返回

<nil>
hello
Run Code Online (Sandbox Code Playgroud)

我希望两者都会返回“hello”,但事实并非如此。我发现该行为在语言规范中作为示例给出。

例如,如果延迟函数是函数文字,并且周围函数具有在文字范围内的命名结果参数,则延迟函数可以在返回结果参数之前访问和修改结果参数。

我的问题是:为什么 defer 对周围函数中定义的变量的工作方式与对命名结果的工作方式不同?这不就是在周围函数返回之前执行的闭包吗?

package main

import (
    "errors"
    "fmt"
)

func main() {

    fmt.Println(up())
    fmt.Println(up2())

}

func up() error {
    var err error
    defer func() {
        err = errors.New("hello")
    }()
    return err
}

func up2() (err error) {
    defer func() {
        err = errors.New("hello")
    }()
    return err
}

func up3() error {
    var err error
    fn := func() {
        err = errors.New("hello")
    }
    fn()
    return err
}
Run Code Online (Sandbox Code Playgroud)

Cer*_*món 5

规范

“defer”语句调用一个函数,该函数的执行被推迟到周围函数返回的那一刻,要么是因为周围函数执行了 return 语句,到达了其函数体的末尾,要么是因为相应的 goroutine 正在恐慌。

一个关键点是,延迟函数是在return语句执行之后执行的。

规范还说

函数F中的“return”语句终止F的执行,并且可选地提供一个或多个结果值。F 推迟的任何函数都会在 F 返回其调用者之前执行。

该函数up返回 nil,因为延迟函数err在 return 语句提供函数结果之后设置。

该函数up2会覆盖 return 语句设置的结果值。