packagemainimport("fmt")funcprintA(aint){fmt.Println("value of a in deferred function",a)}funcmain(){a:=5deferprintA(a)// 收到的 arg 會是這時候當下的 arga=10fmt.Println("value of a before deferred function call",a)}// value of a before deferred function call 10// value of a in deferred function 5
Deferred methods
12345678910111213141516171819202122232425
packagemainimport("fmt")typepersonstruct{firstNamestringlastNamestring}func(pperson)fullName(){fmt.Printf("%s %s",p.firstName,p.lastName)}funcmain(){p:=person{firstName:"John",lastName:"Smith",}deferp.fullName()fmt.Printf("Welcome ")}// Welcome John Smith
packagemainimport("fmt")funcf()(resultint){// 相當於 result = 0,最後再執行 result++,因此最後回傳會是 1deferfunc(){result++}()return0}funcmain(){fmt.Println(f())}// 1
Ex2.
123456789101112131415161718192021
packagemainimport("fmt")funcf()(rint){t:=5// 相當於 r = t,因為最後回傳的參數為 r,但 defer 裡面是針對 t + 5,因此不受影響// 改成 r = t + 5 則會變成 10deferfunc(){t=t+5}()returnt}funcmain(){fmt.Println(f())}// 5
Ex3.
123456789101112131415161718
packagemainimport("fmt")funcf()(rint){// 相當於 r = 1deferfunc(rint){//這裡改的r是傳值傳進去的r,不會改變要返回的那個r值r=r+5}(r)return1}funcmain(){fmt.Println(f())}// 1
// os package 的 Open function return 的值funcOpen(namestring)(file*File,errerror)
123456789101112131415161718
packagemainimport("fmt""os")funcmain(){// 如果 err 是 nil 代表沒有錯誤f,err:=os.Open("/test.txt")iferr!=nil{fmt.Println(err)return}fmt.Println(f.Name(),"opened successfully")}// open /test.txt: No such file or directory
Get more details about an error
1. Asserting the underlying struct type and getting more information from the struct fields
packagemainimport("fmt""os")funcmain(){f,err:=os.Open("/test.txt")// type assertion 斷言 err 是 *os.PathError type,斷言的結果就是 err 的動態值,動態值的 type 就是 *os.PathError// get the underlying *os.PathError value from erriferr,ok:=err.(*os.PathError);ok{fmt.Println("File at path",err.Path,"failed to open")return}fmt.Println(f.Name(),"opened successfully")}// File at path /test.txt failed to open
Golang 的 err.(*os.PathError) 究竟是什麼?
*PathError implements the error interface by declaring the Error() string method
packagemainimport("fmt""net")funcmain(){addr,err:=net.LookupHost("https://www.google.com.tw/")// type assertion 斷言是哪一種 erroriferr,ok:=err.(*net.DNSError);ok{iferr.Timeout(){fmt.Println("operation timed out")}elseiferr.Temporary(){fmt.Println("temporary error")}else{fmt.Println("generic error: ",err)}return}fmt.Println(addr)}// Note: DNS lookups do not work in the playground// generic error: lookup https://www.google.com.tw/: no such host
// Package errors implements functions to manipulate errors.packageerrors// New returns an error that formats as the given text.funcNew(textstring)error{return&errorString{text}}// errorString is a trivial implementation of error.typeerrorStringstruct{sstring}func(e*errorString)Error()string{returne.s}
1234567891011121314151617181920212223242526
packagemainimport("errors""fmt""math")funccircleArea(radiusfloat64)(float64,error){ifradius<0{return0,errors.New("Area calculation failed, radius is less than zero")}returnmath.Pi*radius*radius,nil}funcmain(){radius:=-20.0area,err:=circleArea(radius)iferr!=nil{// err 實作了 error interface 並執行 Error() 拿到回傳的 stringfmt.Println(err)return}fmt.Printf("Area of circle %0.2f",area)}// Area calculation failed, radius is less than zero
2. Adding more information to the error using Errorf
packagemainimport("fmt""math")funccircleArea(radiusfloat64)(float64,error){ifradius<0{return0,fmt.Errorf("Area calculation failed, radius %0.2f is less than zero",radius)}returnmath.Pi*radius*radius,nil}funcmain(){radius:=-20.0area,err:=circleArea(radius)iferr!=nil{fmt.Println(err)return}fmt.Printf("Area of circle %0.2f",area)}// Area calculation failed, radius -20.00 is less than zero
3. Providing more information about the error using struct type and fields
packagemainimport("fmt""math")typeareaErrorstruct{errstringradiusfloat64}// 把 error 的值獨立拉出來,透過外面參數變成一段錯誤訊息func(e*areaError)Error()string{returnfmt.Sprintf("radius %0.2f: %s",e.radius,e.err)}funccircleArea(radiusfloat64)(float64,error){ifradius<0{return0,&areaError{"radius is negative",radius}}returnmath.Pi*radius*radius,nil}funcmain(){radius:=-20.0area,err:=circleArea(radius)iferr!=nil{iferr,ok:=err.(*areaError);ok{fmt.Printf("Radius %0.2f is less than zero",err.radius)return}// assertion fails 就執行這行fmt.Println(err)return}// 都沒錯誤就執行fmt.Printf("Area of rectangle1 %0.2f",area)}// Radius -20.00 is less than zero
4. Providing more information about the error using methods on struct types
ackagemainimport"fmt"typeareaErrorstruct{errstring//error descriptionlengthfloat64//length which caused the errorwidthfloat64//width which caused the error}func(e*areaError)Error()string{returne.err}func(e*areaError)lengthNegative()bool{returne.length<0}func(e*areaError)widthNegative()bool{returne.width<0}funcrectArea(length,widthfloat64)(float64,error){err:=""iflength<0{err+="length is less than zero"}ifwidth<0{iferr==""{err="width is less than zero"}else{err+=", width is less than zero"}}iferr!=""{return0,&areaError{err,length,width}}returnlength*width,nil}funcmain(){length,width:=-5.0,-9.0area,err:=rectArea(length,width)// 這裡回傳回來的 err,是 interface type(因為實作了 Error interface),還不知道是什麼 dynamic value & type// 因此 err.length, err.width, lengthNegative(), widthNegative(),都沒辦法使用,只能使用 Error()iferr!=nil{// 利用 type type assertion to get the underlying value of the error interfaceiferr,ok:=err.(*areaError);ok{// 因此這裡的 err,可以使用 err.length, err.width, lengthNegative(), widthNegative()iferr.lengthNegative(){fmt.Printf("error: length %0.2f is less than zero\n",err.length)}iferr.widthNegative(){fmt.Printf("error: width %0.2f is less than zero\n",err.width)}return}fmt.Println(err)return}fmt.Println("area of rect",area)}/*error: length -5.00 is less than zeroerror: width -9.00 is less than zero*/
packagemainimport("fmt")funcfullName(firstName*string,lastName*string){iffirstName==nil{panic("runtime error: first name cannot be nil")}iflastName==nil{panic("runtime error: last name cannot be nil")}fmt.Printf("%s %s\n",*firstName,*lastName)fmt.Println("returned normally from fullName")}funcmain(){firstName:="Elon"fullName(&firstName,nil)fmt.Println("returned normally from main")}/*panic: runtime error: last name cannot be nilgoroutine 1 [running]:main.fullName(0x1042ff98, 0x0) /tmp/sandbox191402046/main.go:12 +0x140main.main() /tmp/sandbox191402046/main.go:20 +0x40*/
packagemainimport("fmt")funcfullName(firstName*string,lastName*string){deferfmt.Println("deferred call in fullName")iffirstName==nil{panic("runtime error: first name cannot be nil")}iflastName==nil{panic("runtime error: last name cannot be nil")}fmt.Printf("%s %s\n",*firstName,*lastName)fmt.Println("returned normally from fullName")}funcmain(){deferfmt.Println("deferred call in main")firstName:="Elon"fullName(&firstName,nil)fmt.Println("returned normally from main")}/*deferred call in fullNamedeferred call in mainpanic: runtime error: last name cannot be nilgoroutine 1 [running]:main.fullName(0x1042bf90, 0x0) /tmp/sandbox060731990/main.go:13 +0x280main.main() /tmp/sandbox060731990/main.go:22 +0xc0*/
Recover
recover is a builtin function which is used to regain control of a panicking goroutine.
Recover is useful only when called inside deferred functions and called from the same goroutine.
packagemainimport("fmt")funcrecoverName(){ifr:=recover();r!=nil{fmt.Println("recovered from ",r)}}funcfullName(firstName*string,lastName*string){deferrecoverName()iffirstName==nil{panic("runtime error: first name cannot be nil")}iflastName==nil{panic("runtime error: last name cannot be nil")}fmt.Printf("%s %s\n",*firstName,*lastName)fmt.Println("returned normally from fullName")}funcmain(){deferfmt.Println("deferred call in main")firstName:="Elon"fullName(&firstName,nil)fmt.Println("returned normally from main")}/*recovered from runtime error: last name cannot be nilreturned normally from maindeferred call in main*/
packagemainimport("fmt""runtime/debug")funcr(){ifr:=recover();r!=nil{fmt.Println("Recovered",r)debug.PrintStack()}}funca(){deferr()n:=[]int{5,7,4}fmt.Println(n[3])fmt.Println("normally returned from a")}funcmain(){a()fmt.Println("normally returned from main")}/*Recovered runtime error: index out of rangegoroutine 1 [running]:runtime/debug.Stack(0x41c6d8, 0x2, 0x2, 0x6c) /usr/local/go/src/runtime/debug/stack.go:24 +0xc0runtime/debug.PrintStack() /usr/local/go/src/runtime/debug/stack.go:16 +0x20main.r() /tmp/sandbox970455489/main.go:11 +0xc0panic(0xf12a0, 0x19d0b0) /usr/local/go/src/runtime/panic.go:513 +0x240main.a() /tmp/sandbox970455489/main.go:18 +0x60main.main() /tmp/sandbox970455489/main.go:23 +0x20normally returned from main*/