You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go自定义结构体错误中使用errors.Is与errors.As结果不符的问题求助

解决Go中自定义错误与errors.Is/errors.As的匹配问题

咱们来逐个拆解你遇到的问题,一步步梳理清楚原因并给出修复方案:

1. isMyError返回false的核心原因

你的isMyError函数里,每次调用new("")都会生成一个全新的myError实例,而errors.Is默认是做实例相等性匹配(除非你自定义的Is方法有特殊逻辑)。你当前的Is方法把判断逻辑直接委托给了内部的e.err,但这个新创建的myError的内部err,和实际错误链里的err完全不是同一个实例,自然匹配不上。

修复思路

如果你的需求是判断错误链中是否存在myError类型的错误,errors.Is其实不是最合适的工具——它更适合匹配预定义的全局错误实例。判断类型应该用errors.As,调整后的isMyError可以改成这样:

func isMyError(err error) bool {
    var target myError
    return errors.As(err, &target)
}

如果你非要用errors.Is实现类型判断,可以修改myError的Is方法逻辑:

func (e myError) Is(target error) bool {
    // 先判断目标是不是myError类型,是就直接返回true;否则再匹配内部err
    _, ok := target.(myError)
    if ok {
        return true
    }
    return errors.Is(e.err, target)
}

同时isMyError里用空的myError作为匹配目标即可:

func isMyError(err error) bool {
    return errors.Is(err, myError{})
}

2. 最后一行输出panic的原因

当传入的错误不是myError时,errors.As(err, &target)会返回false,此时target还是零值的myError,它的err字段是nil。打印target时会调用它的Error()方法,而e.err.Error()会触发nil指针解引用panic。

修复方案

有两种方式可以避免这个问题:

  • 方式一:在asMyError里判断ok,如果为false就返回nil而非零值myError:
func asMyError(err error) (error, bool) {
    var target myError
    ok := errors.As(err, &target)
    if !ok {
        return nil, ok
    }
    return target, ok
}
  • 方式二:给myError的Error()方法加nil判断,兜底空错误的情况:
func (e myError) Error() string {
    if e.err == nil {
        return "empty myError"
    }
    return e.err.Error()
}

3. 额外优化建议

其实你不需要自己实现As和Is方法——只要实现了Unwrap()方法,标准库的errors.As和errors.Is会自动沿着unwrap链去查找匹配。你自定义的As和Is反而可能干扰默认逻辑,比如你的As方法直接委托给内部err,会让errors.As跳过对myError本身的判断(不过你当前代码里asMyError能匹配到,是因为errors.As会先尝试直接匹配目标类型)。

修复后的完整代码

package main

import (
    "errors"
    "fmt"
)

type myError struct{ err error }

func (e myError) Error() string {
    if e.err == nil {
        return "empty myError"
    }
    return e.err.Error()
}

func new(msg string, args ...any) error {
    return myError{fmt.Errorf(msg, args...)}
}

func (e myError) Unwrap() error {
    return e.err
}

// 移除自定义的As和Is方法,交给标准库处理unwrap链

func isMyError(err error) bool {
    var target myError
    return errors.As(err, &target)
}

func asMyError(err error) (error, bool) {
    var target myError
    ok := errors.As(err, &target)
    if !ok {
        return nil, ok
    }
    return target, ok
}

func main() {
    err := fmt.Errorf("wrap: %w", new("I am a myError"))
    fmt.Println(isMyError(err))
    res, ok := asMyError(err)
    fmt.Println(res, ok)
    err = fmt.Errorf("wrap: %w", errors.New("I am not a myError"))
    fmt.Println(isMyError(err))
    res, ok = asMyError(err)
    fmt.Println(res, ok)
}

修复后的输出

true
I am a myError true
false
<nil> false

内容的提问来源于stack exchange,提问作者Remi.b

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 21:02:39