当defer函数评估其参数时

huangapple go评论78阅读模式
英文:

When defer func evaluates its parameters

问题

我正在学习在Go语言中如何使用defer来处理函数返回时的错误。以下是你提供的代码:

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

文档中提到,参数在defer调用时被评估,这似乎应该是一致的。为什么第二个defer对变量err有不同的值,从而产生不同的输出呢?我知道这与第二个函数将err作为输入参数有关,但不知道具体原因。

英文:

I am learning how defer behaves in golang, and want to use it to handle error when the function returns.

Code is as below:

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
	}
}

The output:

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

Doc says parameters are evaluated when the defer call is evaluated, which seems it should be consistent. Why 2 defer has different value for variable err and thusly different output? I know it is related to 2nd function has err as input parameter, but don't know why.

答案1

得分: 25

好的,以下是翻译好的内容:

好的,我刚刚弄明白了。如果你将任何参数传递给延迟函数(就像上面的第二个延迟函数),这些参数在延迟函数被延迟时进行求值,而不是在它们被执行时。这意味着在我的例子中,err 仍然是 nil,还没有被赋值为一个新的错误。

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

英文:

Ok I just figured out. If you pass any parameters to the defer func (like the 2nd defer function above), those parameters are evaluated when the defer function is deferred, not when they are executed. That means in my example err is still nil and has not been assigned to a new error yet.

on the other hand, in 1st defer above, err is not a parameter, but a variable in function a, and when 1st defer is executed, it has already been assigned to a new error.

答案2

得分: 8

另一种方法是使用对原始 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
英文:

Another way is by using reference to original err variable

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
	}
}

And output is:

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

答案3

得分: 7

以下是翻译好的内容:

Defer语句Defer函数的情况下,还有另一种类似的情况。请看下面的示例:

package main

import (
	"fmt"
	"time"
)

func main() {

	start := time.Now()
	time.Sleep(3*time.Second)
	defer func() { fmt.Println("Defer函数执行时间:", time.Since(start)) }() //Defer函数
	defer fmt.Println("Defer语句执行时间:", time.Since(start)) //Defer语句
	time.Sleep(3*time.Second)
}

输出:

Defer语句执行时间: 3s

Defer函数执行时间: 6s

go play中尝试上述代码。

这是因为在Defer语句的情况下,延迟调用的参数会立即求值。
参考文档

英文:

There is another similar situation there is in case of Defer Statement and Defer Function. Please have look at the example below

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)
}

Output:

> Defer Statement Elapsed Time: 3s
>
> Defer Function Elapsed Time: 6s

Try above in go play

This is because of in the Deferred Statement case the deferred call's arguments are evaluated immediately
refer doc

huangapple
  • 本文由 发表于 2017年3月10日 03:18:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/42703707.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定