1.閉包
Go語言中的閉包同樣也會引用到函數外的變數。閉包的實現確保只要閉包還被使用,那麼被閉包引用的變數會一直存在,例如:
package mainimport "fmt"func main(){ var j int = 5 a := func()(func()) { //圓括弧中的func()表示傳回值是一個func()函數 var i int = 10 return func() { //這裡返回一個匿名函數 fmt.Printf("i, j: %v, %v\n", i, j) } }() //花括弧後帶參數列表表示調用匿名函數,執行到這裡變數a就等於了一個函數了. a() //調用函數a j *= 2 //修改函數外部的變數j a() //再次調用函數a}
運行結果:i, j: 10, 5i, j: 10, 10
在上面的例子中,變數 a 指向的閉包函數引用了局部變數 i 和 j , i 的值被隔離,在閉包外不能被修改,改變 j 的值以後,再次調用 a ,發現結果是修改過的值。在變數 a 指向的閉包函數中,只有內部的匿名函數才能訪問變數 i ,而無法通過其他途徑訪問到,因此保證了 i 的安全性。
2.錯誤處理
2.1 error介面
Go語言引入了一個關於錯誤處理的標準模式,即 error 介面,該介面的定義如下:type error interface { Error() string}建立error通常如下:var e error = errors.New("...")//需要使用使用errors包對於大多數函數,如果要返回錯誤,大致上都可以定義為如下模式,將 error 作為多種傳回值中的最後一個,但這並非是強制要求:
func Foo(param int)(res int,err error){ //....}
調用時的代碼建議按如下方式處理錯誤情況:
n, err := Foo(0)if err != nil { // 錯誤處理} else { // 使用傳回值n}
2.2 defer關鍵字
Go語言中有種不錯的設計,即延遲(defer)語句,你可以在函數中添加多個defer語句。當函數執行到最後時,這些defer語句會按照逆序執行,最後該函數返回。特別是當你在進行一些開啟資源的操作時,遇到錯誤需要提前返回,在返回前你需要關閉相應的資源,不然很容易造成資源流失等問題。
func ReadWrite() bool { file.Open("file") // 做一些工作 if failureX { file.Close() return false } if failureY { file.Close() return false } file.Close() return true}
我們看到上面有很多重複的代碼,Go的defer有效解決了這個問題。使用它後,不但代碼量減少了很多,而且程式變得更優雅。
func ReadWrite() bool { file.Open("file") defer file.Close() //保證資源正常關閉 if failureX { return false } if failureY { return false } return true}
如果有很多調用defer,那麼defer是採用後進先出模式
for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i) //輸出結果:4 3 2 1 0}
defer有點類似java中的try{}finall{}
2.3 panic和recover函數
Go語言有2個內建的函數panic()和recover(),用以報告和捕獲運行時發生的程式錯誤,與error不同,panic和recover一般用在函數內部。一定要注意不要濫用panic和recover,可能會導致效能問題,一般只在未知輸入和不可靠請求時使用。Go語言的錯誤處理流程:當一個函數在執行過程中出現了異常或遇到 panic(),正常語句就會立即終止,然後執行 defer 語句,再報告異常資訊,最後退出 goroutine。如果在defer中使用了recover()函數,則會捕獲錯誤資訊,使該錯誤資訊終止報告。如下樣本,例子來自網路
package mainimport ( "log" //log包 "strconv" //字元轉換包)//捕獲因未知輸入導致的程式異常func catch(nums ...int) int { defer func() { //recover()可以捕獲運行時發生的異常,避免異常時程式直接over,通常用在defer函數內 if r := recover(); r != nil { log.Println("[E]", r) //將捕獲的異常資訊通過log列印,而不會導致程式掛掉 } }() return nums[1] * nums[2] * nums[3] //index out of range}//主動拋出 panic,不推薦使用,可能會導致效能問題func toFloat64(num string) (float64, error) { defer func() { if r := recover(); r != nil { log.Println("[W]", r) } }() if num == "" { panic("param is null") //主動拋出 panic } return strconv.ParseFloat(num, 10) }func main() { catch(2, 8) toFloat64("")}
運行結果:2016/03/26 20:16:03 [E] runtime error: index out of range2016/03/26 20:16:03 [W] param is null
最後補充下nil的介紹:
golang的nil在概念上和其它語言的null、None、nil、NULL一樣,都指代零值或空值。nil是預先說明的標識符,也即通常意義上的關鍵字。在golang中,nil只能賦值給指標、channel、func、interface、map或slice類型的變數。如果未遵循這個規則,則會引發panic。