分享一個在go tour上看到的練習題,練習裏要求用戶本身定義一個錯誤類型,實現error
接口,函數在參數不知足條件的時候返回自定義的錯誤類型的值。練習中特別提示用戶不要在實現的Error
方法裏直接使用fmt.Sprint(e)
以免形成程序內存溢出。git
下面貼一下具體的練習題github
從以前的練習中複製 Sqrt
函數,修改它使其返回 error
值。golang
Sqrt
接受到一個負數時,應當返回一個非 nil 的錯誤值。複數一樣也不被支持。app
建立一個新的類型函數
type ErrNegativeSqrt float64
併爲其實現code
func (e ErrNegativeSqrt) Error() string
方法使其擁有 error
值,經過 ErrNegativeSqrt(-2).Error()
調用該方法應返回 "cannot Sqrt negative number: -2"
。遞歸
注意: 在 Error
方法內調用 fmt.Sprint(e)
會讓程序陷入死循環。能夠經過先轉換 e
來避免這個問題:fmt.Sprint(float64(e))
。這是爲何呢?接口
修改 Sqrt
函數,使其接受一個負數時,返回 ErrNegativeSqrt
值。內存
這裏只爲敘述返回error的狀況,因此請忽略Sqrt函數的功能實現。開發
package main import ( "fmt" ) type ErrNegativeSqrt float64 func (e ErrNegativeSqrt) Error() string { // 這裏直接使用e值會內存溢出 return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e)) } func Sqrt(x float64) (float64, error) { if x < 0 { err := ErrNegativeSqrt(x) return 0, err } return 0, nil } func main() { fmt.Println(Sqrt(2)) fmt.Println(Sqrt(-2)) }
接下來探究一下爲何在練習中把值e
先轉換爲float64類型後程序就不會再內存溢出。
fmt.Sprint(e)
將調用e.Error()
將e
轉換爲字符串。若是Error()
方法調用fmt.Sprint(e)
,則程序將遞歸直到內存溢出。能夠經過將e
轉換成一個非錯誤類型(未實現Error接口)的值來避免這種狀況。
實際上在Error
方法中把error
值直接傳遞給fmt
包中Print相關的函數都會致使無限循環。緣由能夠在fmt包的源碼中找到。
switch verb { case 'v', 's', 'x', 'X', 'q': // Is it an error or Stringer? // The duplication in the bodies is necessary: // setting wasString and handled, and deferring catchPanic, // must happen before calling the method. switch v := p.field.(type) { case error: wasString = false handled = true defer p.catchPanic(p.field, verb) // 這裏調用了Error方法 p.printField(v.Error(), verb, plus, false, depth) return
經過連接能夠在Github上看到這塊詳細的源碼 https://github.com/golang/go/...
這個練習感受仍是給開發者提示了一個很是隱蔽的坑,感興趣的能夠去go tour上的這個練習題本身試驗一下。