为什么Go的自定义error有时候会内存溢出

分享一个在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函数的功能实现。

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/blob/2ed57a8cd86cec36b8370fb16d450e5a29a9375f/src/pkg/fmt/print.go#L639

这个练习感觉还是给开发者提示了一个非常隐蔽的坑,感兴趣的可以通过阅读原文的链接访问到go tour上的这个练习题自己试验一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值