这个是 Go 里面最经典的坑之一。几乎所有 Go 新人 都会踩甚至很多工作几年的人都容易写错。因为真正难点不是 error。而是interface 底层机制今天我们彻底讲透。一、error 到底是什么很多人以为error 是特殊类型。其实不是。它本质就是interface源码typeerrorinterface{Error()string}二、什么意思只要一个类型实现了Error()string它就自动实现error接口三、例如typeMyErrorstruct{}现在给它实现func(e*MyError)Error()string{return出错了}四、这一刻发生了什么重点因为*MyError拥有Error()string所以Go 自动认为*MyError 实现了 error 接口五、于是就能这样写varerrerrorerrMyError{}六、为什么不需要 implementsGo接口实现是隐式实现不是Java 那种显式 implements七、真正核心重点Go只看你有没有这个方法有就算实现接口。八、现在来到最经典坑重点代码funcfoo()error{varp*MyErrornilreturnp}九、mainfuncmain(){err:foo()fmt.Println(errnil)}十、很多人以为输出什么很多人会觉得true因为pnil十一、但真正输出false十二、为什么真正核心因为error 是 interface而interface 底层 (Type, Value)十三、interface 底层结构必须理解interface实际上像(T, V)T类型。V值。十四、例如varxinterface{}123底层T int V 123十五、再看你的代码varp*MyErrornil这里T *MyError V nil十六、return p 时发生什么重点因为函数返回值是error所以Go会把p装进interface十七、于是 err 实际变成err ( T *MyError, V nil )十八、重点来了超级重要真正的nil interface必须T nil V nil十九、而现在你的T *MyError并不是 nil。二十、所以整个 interface 不等于 nil于是errnil结果false二十一、真正底层图重点真正 nil interfaceinterface ├── T nil └── V nil你的 errinterface ├── T *MyError └── V nil二十二、所以为什么叫“假 nil”因为值是 nil但接口本身不是 nil二十三、这是 Go 面试超级经典题因为很多人根本不知道interface 有类型信息二十四、真正危险在哪里例如错误代码funcfoo()error{varp*MyErrornilreturnp}调用方err:foo()iferr!nil{fmt.Println(发生错误)}二十五、会发生什么明明没有错误却进入发生错误逻辑。二十六、这就是线上 BUG 来源因为开发者以为返回的是 nil实际上不是。二十七、正确写法重点正确方式funcfoo()error{varp*MyErrornilifpnil{returnnil}returnp}二十八、为什么这样就对了因为这里returnnil返回的是真正的 nil interface也就是T nil V nil二十九、于是errnil结果才是true三十、真正核心理解重点错误写法returnp返回(*MyError, nil)正确写法returnnil返回(nil, nil)三十一、为什么 Go 要这样设计因为interface必须知道里面装的是什么类型否则Go无法调用Error()三十二、真正本质一句话重点interface永远保存动态类型 动态值三十三、再举个经典例子varp*intnilvarxinterface{}p fmt.Println(xnil)三十四、输出false三十五、原因完全一样因为x ( T *int, V nil )三十六、Go 官方最经典一句话很多 Go 老手都会说带类型的 nil ! nil interface三十七、最后一句总结必须记住error本质就是interfaceinterface 底层(Type, Value)真正 nil interface(Tnil, Vnil)假 nil(T*MyError, Vnil)因为类型不为空所以err!nil真正核心带类型的 nil 不等于 空 interface