為什麼Go的自定義error有時候會內存溢出

2019-10-14     Go語言中文網

分享一個在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語言中文網」:

本文作者:KevinYan11

原文連結:https://mp.weixin.qq.com/s/HZCLtIPQ8OsYgmBrVskTUQ

文章來源: https://twgreatdaily.com/ooqUy20BMH2_cNUgAtRb.html