分享一個在go tour上看到的練習(xí)題,練習(xí)里要求用戶自己定義一個錯誤類型,實現(xiàn) error接口,函數(shù)在參數(shù)不滿足條件的時候返回自定義的錯誤類型的值。練習(xí)中特別提示用戶不要在實現(xiàn)的 Error方法里直接使用 fmt.Sprint(e)以避免造成程序內(nèi)存溢出。
下面貼一下具體的練習(xí)題
Practice
從之前的練習(xí)中復(fù)制 Sqrt 函數(shù),修改它使其返回 error 值。
Sqrt 接受到一個負數(shù)時,應(yīng)當(dāng)返回一個非 nil 的錯誤值。復(fù)數(shù)同樣也不被支持。
創(chuàng)建一個新的類型
type ErrNegativeSqrt float64
并為其實現(xiàn)
func (e ErrNegativeSqrt) Error() string
方法使其擁有 error 值,通過 ErrNegativeSqrt(-2).Error() 調(diào)用該方法應(yīng)返回 "cannot Sqrt negative number: -2"。
注意: 在 Error 方法內(nèi)調(diào)用 fmt.Sprint(e) 會讓程序陷入死循環(huán)。可以通過先轉(zhuǎn)換 e 來避免這個問題:fmt.Sprint(float64(e))。這是為什么呢?
修改 Sqrt 函數(shù),使其接受一個負數(shù)時,返回 ErrNegativeSqrt 值。
Solution
這里只為敘述返回error的情況,所以請忽略Sqrt函數(shù)的功能實現(xiàn)。

接下來探究一下為什么在練習(xí)中把值 e先轉(zhuǎn)換為float64類型后程序就不會再內(nèi)存溢出。
fmt.Sprint(e)將調(diào)用 e.Error()將 e轉(zhuǎn)換為字符串。如果 Error()方法調(diào)用 fmt.Sprint(e),則程序?qū)⑦f歸直到內(nèi)存溢出。可以通過將 e轉(zhuǎn)換成一個非錯誤類型(未實現(xiàn)Error接口)的值來避免這種情況。
實際上在 Error方法中把 error值直接傳遞給 fmt包中Print相關(guān)的函數(shù)都會導(dǎo)致無限循環(huán)。原因可以在fmt包的源碼中找到。

通過鏈接可以在Github上看到這塊詳細的源碼 https://github.com/golang/go/blob/2ed57a8cd86cec36b8370fb16d450e5a29a9375f/src/pkg/fmt/print.go#L639
這個練習(xí)感覺還是給開發(fā)者提示了一個非常隱蔽的坑,感興趣的可以通過閱讀原文的鏈接訪問到go tour上的這個練習(xí)題自己試驗一下。
喜歡本文的朋友,歡迎關(guān)注“Go語言中文網(wǎng)”: