分享一個在go tour上看到的練習題,練習里要求用戶自己定義一個錯誤類型,實現 error接口,函數在參數不滿足條件的時候返回自定義的錯誤類型的值。練習中特別提示用戶不要在實現的 Error方法里直接使用 fmt.Sprint(e)以避免造成程序內存溢出。
下面貼一下具體的練習題
Practice
從之前的練習中復制 Sqrt 函數,修改它使其返回 error 值。
Sqrt 接受到一個負數時,應當返回一個非 nil 的錯誤值。復數同樣也不被支持。
創建一個新的類型
type ErrNegativeSqrt float64
并為其實現
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 值。
Solution
這里只為敘述返回error的情況,所以請忽略Sqrt函數的功能實現。
接下來探究一下為什么在練習中把值 e先轉換為float64類型后程序就不會再內存溢出。
fmt.Sprint(e)將調用 e.Error()將 e轉換為字符串。如果 Error()方法調用 fmt.Sprint(e),則程序將遞歸直到內存溢出。可以通過將 e轉換成一個非錯誤類型(未實現Error接口)的值來避免這種情況。
實際上在 Error方法中把 error值直接傳遞給 fmt包中Print相關的函數都會導致無限循環。原因可以在fmt包的源碼中找到。
通過鏈接可以在Github上看到這塊詳細的源碼 https://github.com/golang/go/blob/2ed57a8cd86cec36b8370fb16d450e5a29a9375f/src/pkg/fmt/print.go#L639
這個練習感覺還是給開發者提示了一個非常隱蔽的坑,感興趣的可以通過閱讀原文的鏈接訪問到go tour上的這個練習題自己試驗一下。
喜歡本文的朋友,歡迎關注“Go語言中文網”: