简介和 Java、Python 的 try-catch 异常机制不同Go 语言采用的是 错误值机制 error。Go 没有异常抛出、捕获机制所有错误都是普通返回值。本文带你从零彻底吃透 Go errorerror 底层本质是什么如何创建错误自定义业务错误错误包装 %w核心重点errors.Is / errors.As 实战用法经典 error nil 坑点error 和 panic 区别与规范一、error 到底是什么底层本质error 是 Go 内置的一个接口源码超级简单type error interface {Error() string}只要一个类型实现了 Error() string 方法它就是 error。特点error 是一个普通返回值不是异常nil 代表无错误非 nil 代表出错二、两种基础创建 error 方式errors.New固定错误文本packagemainimport(errorsfmt)funcmain(){err:errors.New(用户名不能为空)iferr!nil{fmt.Println(err)}}fmt.Errorf支持动态拼接name:err:fmt.Errorf(用户名 %s 不合法,name)fmt.Println(err)三、自定义错误项目必备原生 error 只能存字符串无法携带 错误码、字段、错误类型。项目中必须使用 自定义结构体 error。// 自定义参数校验错误typeValidateErrorstruct{Fieldstring// 出错字段Msgstring// 错误信息}// 实现 error 接口func(e*ValidateError)Error()string{returnfmt.Sprintf(字段[%s]错误%s,e.Field,e.Msg)}funcCheckAge(ageint)error{ifage0{returnValidateError{Field:age,Msg:年龄不能为负数,}}returnnil}这样我们后续可以精准判断「是什么类型的错误」。四、重点错误包装 %wGo1.13 核心场景多层函数调用底层报错上层需要追加信息同时保留原始错误。两种包装方式巨大区别%v仅拼接字符串丢失原始错误无法判断错误类型%w包装原始错误保留错误链项目标准用法正确示范// 底层错误varErrUserNotExisterrors.New(用户不存在)funcqueryUser()error{returnErrUserNotExist}funccreateOrder()error{err:queryUser()iferr!nil{// 包装原始错误保留错误链returnfmt.Errorf(创建订单失败%w,err)}returnnil}通过 %w 包装后上层可以用errors.Is、errors.As 识别底层根源错误。五、errors.Is 和 errors.As 精讲面试高频errors.Is判断是否是某个「哨兵错误」作用遍历错误链判断底层根源是不是目标错误。funcmain(){err:createOrder()iferrors.Is(err,ErrUserNotExist){fmt.Println(根源错误用户不存在执行兜底逻辑)}}errors.As判断是否是某个「自定义错误类型」作用匹配自定义结构体错误并取出内部字段。funcmain(){err:CheckAge(-10)// 接收自定义错误varvErr*ValidateErroriferrors.As(err,vErr){fmt.Printf(错误字段%s原因%s\n,vErr.Field,vErr.Msg)}}一句话区分errors.Is匹配 错误值哨兵错误errors.As匹配 错误类型自定义结构体六、经典坑点error nil 陷阱必踩坑非常经典的面试题typeMyErrstruct{}func(e*MyErr)Error()string{return出错了}functest()error{vare*MyErrnilreturne// ❌ 看似 nil实际不是 nil}funcmain(){err:test()iferr!nil{fmt.Println(居然进入错误分支)}}原因Go 接口变量 类型 值值是 nil但类型是 *MyErr所以整个 error 接口 不等于 nil解决方案业务函数无错误时直接 return nil不要返回结构体空指针。七、error 和 panic 的使用规范error日常 99% 场景处理可预期、正常业务失败参数错误数据不存在网络请求失败数据库操作失败panic极少用处理不可恢复的程序bug配置文件缺失依赖初始化失败数组越界、断言失败业务代码禁止 panic八、项目开发最佳实践总结简单错误用 errors.New()动态信息用 fmt.Errorf()多层调用包装错误 必须用 %w判断根源错误用 errors.Is获取自定义错误详情用 errors.As禁止丢弃错误禁止_ func()业务错误统一返回 error不随意 panic九、完整综合 Demopackagemainimport(errorsfmt)// 1. 定义哨兵错误varErrNotFounderrors.New(数据不存在)// 2. 自定义错误typeBizErrorstruct{CodeintMsgstring}func(b*BizError)Error()string{returnfmt.Sprintf(code:%d, msg:%s,b.Code,b.Msg)}// 底层函数funcfindData()error{returnErrNotFound}// 上层包装funcloadData()error{err:findData()iferr!nil{returnfmt.Errorf(加载数据失败: %w,err)}returnnil}funcmain(){err:loadData()iferr!nil{// 判断根源错误iferrors.Is(err,ErrNotFound){fmt.Println(识别到数据不存在)}// 判断自定义业务错误varbe*BizErroriferrors.As(err,be){fmt.Println(业务错误码,be.Code)}}} 结语Go 的 error 看着简单实则细节极多尤其是错误链、%w、Is/As 是面试和项目的高频重点。本文可以收藏作为 Go 错误处理学习手册欢迎点赞、收藏、关注持续更新 Go 核心知识