当 defer func 评估其参数时

When defer func evaluates its parameters

我正在学习 defer 在 golang 中的行为方式,并希望在函数 returns.

时使用它来处理错误

代码如下:

package main

import "fmt"
import "errors"

func main() {
    a()
}

func a() {
    var err error   
    defer func(){
        if err != nil {
            fmt.Printf("1st defer: %s\n", err)
        } else {
            fmt.Println("1st defer: defer not error")
        }
    }()
    defer func(err error){
        if err != nil {
            fmt.Printf("2nd defer: %s\n", err)
        } else {
            fmt.Println("2nd defer: defer not error")
        }
    }(err)

    err = errors.New("new error")
    if err != nil {
        return
    }
}

输出:

2nd defer: defer not error
1st defer: new error

Doc说参数是在defer调用求值的时候求值的,看来应该是一致的。为什么 2 defer 对变量 err 有不同的值,因此输出不同?我知道它与第二个函数有关 err 作为输入参数,但不知道为什么。

好的,我刚刚明白了。如果您将任何参数传递给延迟函数(如上面的第二个延迟函数),这些参数在延迟函数被延迟时计算,而不是在它们被执行时计算。这意味着在我的示例中 err 仍然是 nil 并且尚未分配给新错误。

另一方面,在上面的第一个defer中,err不是参数,而是函数a中的一个变量,当第一个defer执行时,它已经被赋值给了一个新错误。

另一种方法是使用对原始 err 变量的引用

package main

import (
    "errors"
    "fmt"
)

func main() {
    a()
}

func a() {
    var err error
    defer func() {
        if err != nil {
            fmt.Printf("1st defer: %s\n", err)
        } else {
            fmt.Println("1st defer: defer not error")
        }
    }()
    defer func(err *error) {
        if *err != nil {
            fmt.Printf("2nd defer: %s\n", *err)
        } else {
            fmt.Println("2nd defer: defer not error")
        }
    }(&err)

    err = errors.New("new error")
    if err != nil {
        return
    }
}

输出为:

2nd defer: new error
1st defer: new error

Defer StatementDefer Function的情况下还有另一种类似的情况。请看下面的例子

package main

import (
    "fmt"
    "time"
)

func main() {

    start := time.Now()
    time.Sleep(3*time.Second)
    defer func() { fmt.Println("Defer Function Elapsed Time: ", time.Since(start)) }() //Defer Function
    defer fmt.Println("Defer Statement Elapsed Time: ", time.Since(start)) //Defer Statement
    time.Sleep(3*time.Second)
}

输出:

Defer Statement Elapsed Time: 3s

Defer Function Elapsed Time: 6s

go play

中尝试上面的方法

这是因为在 Deferred Statement 的情况下,延迟调用的参数会立即计算 refer doc