如何理解 Go 语言中的 `defer`?

5
在godoc(https://blog.golang.org/defer-panic-and-recover)中,有一个例子:
  1. 延迟函数可以读取和赋值给返回函数的命名返回值。

在这个例子中,一个延迟函数在周围函数返回后增加了返回值i。 因此,该函数返回2:

func c() (i int) {
    defer func() { i++ }()
    return i
}

我还写了一个小程序:

package main

import "fmt"

func b() int {
    i := 0
    for ; i < 4; i++ {
        defer func() {fmt.Println(i); i++} ()
    }
    return i
}
func main() {
  fmt.Println("result = ", b())
}

输出结果为:

4
5
6
7
result =  4

所以我很困惑,为什么第二个例子没有输出8

看看这个,可能会有帮助:https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - Inanc Gumus
3个回答

10

请注意提到的部分,即“可以读取并赋值给返回函数的命名返回值”。

这意味着:

func b() int {
    var i int
    defer func() { fmt.Println(i); i++ }()
    return i
}

将会输出0result = 0,而:

func b() (i int) {
    defer func() { fmt.Println(i); i++ }()
    return i
}

将会输出 0,并且 result = 1

可以想象,在第一个例子中,return ii 的值分配给了一个隐藏的返回变量(因为它没有命名),然后继续执行 defer 语句(这些语句只修改局部变量 i)。而在第二个例子中,我们直接赋值给返回变量(因为它有名字),所以 defer 语句能够改变它。

基本上,您的程序可以按照以下方式解释:

package main

import "fmt"

func b() (hiddenVariable int) {
    i := 0
    for ; i < 4; i++ {
        defer func() { fmt.Println(i); i++ }()
    }
    hiddenVariable = i; return // implicit meaning of return i
}

func main() {
    fmt.Println("result = ", b())
}

0

基于我对延迟(defer)的理解以及查看了你的代码,我想说,for循环延迟打印后续的 'i',但是for循环仍然会运行,因此会影响函数b()返回的 'i',它是4。

for ; i < 4; i++ {
    defer func() {fmt.Println(i); i++} ()
}
return i

由于打印语句及其对'i'的值的延迟,它们会递增超过4,但是for循环后面的'i'仍然保持在4。


0

defer语句将一个函数调用推入列表中。在包含该语句的函数返回后,保存的函数调用列表会被执行。

你的func b()在语义上等同于:

func b() int {
    i := 0
    for ; i < 4; i++ {
    }
    ret := i
    fmt.Println(i); i++
    fmt.Println(i); i++
    fmt.Println(i); i++
    fmt.Println(i); i++
    return ret
}

Ergo b() 返回 4。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接