defer可以讀取有名返回值
func c() (i int) { defer func() { i++ }() return 1 }
輸出結果是2. 在開頭的時候,我們知道defer是在return調用之后才執行的。 這里需要明確的是defer代碼塊的作用域仍然在函數之內,結合上面的函數也就是說,defer的作用域仍然在c函數之內。因此defer仍然可以讀取c函數內的變量(如果無法讀取函數內變量,那又如何進行變量清除呢....)。
當執行return 1 之后,i的值就是1. 此時此刻,defer代碼塊開始執行,對i進行自增操作。 因此輸出2.
A. 無名返回值的情況
package main import ( "fmt" ) func main() { fmt.Println("return:", a()) // 打印結果為 return: 0 } func a() int { var i int defer func() { i++ fmt.Println("defer2:", i) // 打印結果為 defer: 2 }() defer func() { i++ fmt.Println("defer1:", i) // 打印結果為 defer: 1 }() return i }
B. 有名返回值的情況
package main import ( "fmt" ) func main() { fmt.Println("return:", b()) // 打印結果為 return: 2 } func b() (i int) { defer func() { i++ fmt.Println("defer2:", i) // 打印結果為 defer: 2 }() defer func() { i++ fmt.Println("defer1:", i) // 打印結果為 defer: 1 }() return i // 或者直接 return 效果相同 }
先來假設出結論,幫助大家理解
-
多個defer的執行順序為“后進先出”;
-
defer、return、返回值三者的執行邏輯應該是:return最先執行,return負責將結果寫入返回值中;接着defer開始執行一些收尾工作;最后函數攜帶當前返回值退出。
如何解釋兩種結果的不同:
上面兩段代碼的返回結果之所以不同,其實從上面第2條結論很好理解。
a()int 函數的返回值沒有被提前聲明,其值來自於其他變量的賦值,而defer中修改的也是其他變量,而非返回值本身,因此函數退出時返回值並沒有被改變。
b()(i int) 函數的返回值被提前聲明,也就意味着defer中是可以調用到真實返回值的,因此defer在return賦值返回值 i 之后,再一次地修改了 i 的值,最終函數退出后的返回值才會是defer修改過的值。
C. 下面我們再來看第三個例子,驗證上面的結論:
package main import ( "fmt" ) func main() { fmt.Println("c return:", *(c())) // 打印結果為 c return: 2 } func c() *int { var i int defer func() { i++ fmt.Println("c defer2:", i) // 打印結果為 c defer: 2 }() defer func() { i++ fmt.Println("c defer1:", i) // 打印結果為 c defer: 1 }() return &i }
雖然 c()*int 的返回值沒有被提前聲明,但是由於 c()*int 的返回值是指針變量,那么在return將變量 i 的地址賦給返回值后,defer再次修改了 i 在內存中的實際值,因此函數退出時返回值雖然依舊是原來的指針地址,但是其指向的內存實際值已經被成功修改了。
即,我們假設的結論是正確的!