含有
defer
語句的函數,會在該函數将要傳回之前,調用另一個函數。簡單點說就是
defer
語句後面跟着的函數會延遲到目前函數執行完後再執行。
下面是一個簡單的例子:
package main
import "fmt"
func myPrint() {
fmt.Println("Go")
}
func main() {
defer myPrint()
fmt.Println("Let's")
}
首先,執行
main
函數,因為
myPrint()
函數前有
defer
關鍵字,是以會在執行完
main
函數後再執行
myPrint()
函數,是以先列印出
Let's
,再執行
myPrint()
函數列印
Go
。運作該程式輸出如下:
Let's
Go
上面的程式等價于下面的程式:
defer fmt.Println("Go")
fmt.Println("Let's")
即時求值的變量快照
使用
defer
隻是延時調用函數,傳遞給函數裡的變量,不應該受到後續程式的影響。
str := "Go"
defer fmt.Println(str)
str = "Let's"
fmt.Println(str)
同理,運作該程式會輸出如下:
Let's
Go
延遲方法
defer
不僅能夠延遲函數的執行,也能延遲方法的執行。
package main
import "fmt"
type Person struct {
firstName, lastName string
}
func (p Person) printName() {
fmt.Printf("%s %s", p.firstName, p.lastName)
}
func main() {
p := Person{"John", "Smith"}
defer p.printName()
fmt.Printf("Hello ")
}
運作該程式輸出如下:
Hello John Smith
defer 棧
當一個函數内多次調用
defer
時,Go 會把
defer
調用放入到一個棧中,随後按照 後進先出(Last In First Out, LIFO) 的順序執行。
package main
import "fmt"
func main() {
defer fmt.Printf("Caizi.")
defer fmt.Printf("am ")
defer fmt.Printf("I ")
fmt.Printf("Hello! ")
}
運作上面的程式輸出如下:
Hello! I am Caizi.
defer 在 return 後調用
看看下面的例子你就知道了:
package main
import "fmt"
var x int = 100
func myfunc() int {
defer func() {x = 200}()
fmt.Println("myfunc: x =", x)
return x
}
func main() {
myx := myfunc()
fmt.Println("main: x =", x)
fmt.Println("main: myx =", myx)
}
運作該程式輸出如下:
myfunc: x = 100
main: x = 200
main: myx = 100
defer 可以使代碼更簡潔
如果沒有使用
defer
,當在一個操作資源的函數裡調用多個
return
時,每次都得釋放資源,你可能這樣寫代碼:
func f() {
r := getResource() //0,擷取資源
......
if ... {
r.release() //1,釋放資源
return
}
......
if ... {
r.release() //2,釋放資源
return
}
......
if ... {
r.release() //3,釋放資源
return
}
......
r.release() //4,釋放資源
return
}
有了
defer
之後,你可以簡潔地寫成下面這樣:
func f() {
r := getResource() //0,擷取資源
defer r.release() //1,釋放資源
......
if ... {
...
return
}
......
if ... {
...
return
}
......
if ... {
...
return
}
......
return
}
參考文獻:
[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程式設計語言, Translated by 李道兵, 高博, 龐向才, 金鑫鑫 and 林齊斌, 機械工業出版社, 2017.