https://inasa.dev/blog/rss.xml

Go | 协程和延迟调用的实参的估值时刻

2024-03-09
  • 一个延迟调用的实参是在此调用对应的延迟调用语句被执行时被估值的。 或者说,它们是在此延迟调用被推入延迟调用队列时被估值的。 这些被估值的结果将在以后此延迟调用被执行的时候使用。
  • 一个匿名函数体内的表达式是在此函数被执行的时候才会被逐渐估值的,不管此函数是被普通调用还是延迟/协程调用。
package main 

import "fmt" 

func main() { 
    func() { 
        for i := 0; i < 3; i++ { 
            defer fmt.Println("a:", i) 
        } 
    }() 
    fmt.Println() 
    func() { 
        for i := 0; i < 3; i++ { 
            defer func() { 
                fmt.Println("b:", i) 
            }() 
        } 
    }() 
} 

输出

a: 2 
a: 1 
a: 0 

b: 3 
b: 3 
b: 3 
  • 第一个循环中的i是在fmt.Println函数调用被推入延迟调用队列的时候估的值
  • 第二个循环中的i是在第二个匿名函数调用的退出阶段估的值(此时循环变量i的值已经变为3)

修改1

for i := 0; i < 3; i++ { 
    defer func(i int) { 
        // 此i为形参i,非实参循环变量i。 
        fmt.Println("b:", i) 
    }(i) 
} 

修改2

for i := 0; i < 3; i++ { 
    i := i // 在下面的调用中,左i遮挡了右i。 
    // <=> var i = i 
    defer func() { 
        // 此i为上面的左i,非循环变量i。 
        fmt.Println("b:", i) 
    }() 
}

  • Go101