分享一個在go tour上看到的練習題,練習裏要求用戶本身定義一個錯誤類型,實現error
接口,函數在參數不知足條件的時候返回自定義的錯誤類型的值。練習中特別提示用戶不要在實現的Error
方法裏直接使用fmt.Sprint(e)
以免形成程序內存溢出。git
下面貼一下具體的練習題github
從以前的練習中複製 Sqrt
函數,修改它使其返回 error
值。golang
Sqrt
接受到一個負數時,應當返回一個非 nil 的錯誤值。複數一樣也不被支持。bash
建立一個新的類型app
type ErrNegativeSqrt float64
複製代碼
併爲其實現函數
func (e ErrNegativeSqrt) Error() string
複製代碼
方法使其擁有 error
值,經過 ErrNegativeSqrt(-2).Error()
調用該方法應返回 "cannot Sqrt negative number: -2"
。ui
注意: 在 Error
方法內調用 fmt.Sprint(e)
會讓程序陷入死循環。能夠經過先轉換 e
來避免這個問題:fmt.Sprint(float64(e))
。這是爲何呢?spa
修改 Sqrt
函數,使其接受一個負數時,返回 ErrNegativeSqrt
值。code
這裏只爲敘述返回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上看到這塊詳細的源碼 github.com/golang/go/b…
這個練習感受仍是給開發者提示了一個很是隱蔽的坑,感興趣的能夠去go tour上的這個練習題本身試驗一下。