這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
defer
文法:
defer function_name()
簡單來講,在defer所在函數執行完所有的代碼之後,會自動執行defer的這個函數。
樣本一(準系統)
package mainimport "fmt"/*D:\examples>go run helloworld.gofirstsecondD:\examples>*/func main() { defer second() first()}func first() { fmt.Println("first")}func second() { fmt.Println("second")}
樣本二(函數局部性)
package mainimport "fmt"/*D:\examples>go run helloworld.gofirstthirdfirstsecondD:\examples>*/func main() { defer second() first() third()}func first() { fmt.Println("first")}func second() { fmt.Println("second")}func third() { defer first() fmt.Println("third")}
樣本三(棧特性)
package mainimport "fmt"/*D:\examples>go run helloworld.gofirstthirdsecondD:\examples>*/func main() { defer second() defer third() first()}func first() { fmt.Println("first")}func second() { fmt.Println("second")}func third() { fmt.Println("third")}
使用情境
主要用於資源需要釋放的情境。比如開啟一個檔案,最後總是要關閉的。而在開啟和關閉之間,會有諸多的處理,可能會有諸多的if-else、根據不同的情況需要提前返回。在傳統語言中,return之前都需要一一調用close()。
而Go的defer就將事情變得簡單了,open()之後,直接就用defer“註冊”一個close()。虛擬碼:
f, = os.open(filename)defer f.close()do_something()if (condition_a) {return}do_something_again() if (condition_b) {return}do_further_things()
panic & recover
先給出https://golang.org/pkg/builtin/上的函數說明。
panic
func panic(v interface{})
The panic built-in function stops normal execution of the current goroutine. When a function F calls panic, normal execution of F stops immediately. Any functions whose execution was deferred by F are run in the usual way, and then F returns to its caller. To the caller G, the invocation of F then behaves like a call to panic, terminating G’s execution and running any deferred functions. This continues until all functions in the executing goroutine have stopped, in reverse order. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking and can be controlled by the built-in function recover.
recover
func recover() interface{}
The recover built-in function allows a program to manage behavior of a panicking goroutine. Executing a call to recover inside a deferred function (but not any function called by it) stops the panicking sequence by restoring normal execution and retrieves the error value passed to the call of panic. If recover is called outside the deferred function it will not stop a panicking sequence. In this case, or when the goroutine is not panicking, or if the argument supplied to panic was nil, recover returns nil. Thus the return value from recover reports whether the goroutine is panicking.
要點
- panic相當於一個運行時異常
- 遇到panic的時候,會停止當前函數剩下來的語句,但在退出該函數之前,會執行defer的語句
- 依據函數調用層次,panic依次終止每個函數,直至main()。
panic樣本
package mainimport "fmt"/*D:\examples>go run helloworld.gof.1g.1h.1h.defer()g.defer()panic: panic in h()goroutine 1 [running]:panic(0x495360, 0xc04203a230) C:/Go/src/runtime/panic.go:500 +0x1afmain.h() D:/examples/helloworld.go:54 +0x12bmain.g() D:/examples/helloworld.go:45 +0xeemain.f() D:/examples/helloworld.go:38 +0xabmain.main() D:/examples/helloworld.go:29 +0x1bexit status 2D:\examples>*/func main() { f() // Line Number: 29}func final_print(msg string) { fmt.Println(msg)}func f() { fmt.Println("f.1") g() // Line Number: 38 fmt.Println("f.2")}func g() { defer final_print("g.defer()") fmt.Println("g.1") h() // Line Number: 45 fmt.Println("g.2")}func h() { defer final_print("h.defer()") fmt.Println("h.1") panic("panic in h()") // Line Number: 52 fmt.Println("h.2")}
panic & defer & recover
recover相當於try-catch的catch部分,使得panic不再傳遞。而defer相當於try-catch-final的final部分。
package mainimport "fmt"/*D:\examples>go run helloworld.gof.1g.1h.1h.defer()g.defer()panic in h()f.2D:\examples>*/func main() { f()}func final_print(msg string) { fmt.Println(msg)}func f() { fmt.Println("f.1") g() fmt.Println("f.2")}func g() { defer func() { str := recover() fmt.Println(str) }() defer final_print("g.defer()") fmt.Println("g.1") h() fmt.Println("g.2")}func h() { defer final_print("h.defer()") fmt.Println("h.1") panic("panic in h()") fmt.Println("h.2")}
擷取數組元素
接下來再給一個例子,擷取數組元素,處理數組訪問越界的問題。
package mainimport "fmt"/*D:\examples>go run helloworld.goa[0]=1[true]a[1]=2[true]a[2]=3[true]a[3]=4[true]a[4]=5[true]runtime error: index out of range [set to default value -1]a[5]=-1[false]runtime error: index out of range [set to default value -1]a[6]=-1[false]runtime error: index out of range [set to default value -1]a[7]=-1[false]runtime error: index out of range [set to default value -1]a[8]=-1[false]runtime error: index out of range [set to default value -1]a[9]=-1[false]D:\examples>*/func main() { a := [5]int {1,2,3,4,5} for i := 0; i < 10; i++ { item, ok := get(i, a) fmt.Printf("a[%d]=%d[%v]\n", i, item, ok) }}func get(i int, a [5]int) (ret int, ok bool) { ok = true defer func() { err := recover() if err != nil { fmt.Println(err, "[set to default value -1]") ret = -1 ok = false } }() ret = a[i] return}