你的Go代码不需要那么多nil检查
凌晨两点半,我把咖啡杯往桌上一搁,盯着屏幕上那个PR发呆。四百多行代码,我数了数,光if x == nil就出现了三十七次。说实话,我当时第一反应不是觉得有问题——而是觉得这哥们儿挺细心的。嗯,防御性编程嘛,多多益善。我原本以为nil检查写得越多代码就越安全,后来发现完全不是那么回事。

这篇文章是Konrad Reiche在六月十六号发的,HN上一百零二人点赞,七十八条评论,吵得挺凶。核心论点其实就一句话:你的代码里到处塞满nil检查,不代表你的代码健壮——恰恰相反,这说明你已经搞不清楚什么东西到底能为nil、什么东西不能为nil了。这话扎心。
怎么说呢,这个观点第一次看到的时候我是有点不服气的。你想啊,一个nil check才几个字节的事,加上又不会多花钱,万一真的panic了呢?但Konrad举了个例子把我说服了。他写了个RateLimiter结构体,里面有个Redis client作为依赖。结果呢,构造函数里——注意是构造函数里,就是你创建这个对象的地方——居然还在检查Redis client是不是nil。这不扯吗?你的构造函数本身就要求传一个非nil的client进来,那你在方法里再做nil检查,等于告诉读代码的人"这个东西有可能是nil哦"。但它根本不应该为nil。你把一个不可能发生的事情写得好像随时会发生一样,这不是防御,这是误导。
我揉了揉眼睛,把那个PR的diff往回翻了翻,发现写代码的人其实在三个不同的方法里检查了同一个字段是否为nil,而这三个方法的调用链是同一条——也就是说如果第一个方法里那个字段不为nil,后面两个方法里它也不可能突然变成nil。
你看,这就是问题所在。nil check本身是廉价的——编译不要钱,运行也就一个分支判断——但它传递的信号是昂贵的。下一个读你代码的人,可能是三个月后的你自己,看到那个nil check会想:这里是不是有什么历史原因?是不是某个caller真的会传nil进来?然后他就开始顺着这条错误的路径去理解你的整个设计,最后要么加更多无意义的防御代码,要么写出更混乱的逻辑。反正就是一团糟。

Konrad还提到一个现象,我觉得特别真实:这种过度nil检查的模式在AI生成的代码里尤其常见,但也不局限于AI。人也会这么干,尤其是新手,尤其是那种被线上panic吓过的人。我自己就被吓过。去年有个生产事故,就是因为一个中间件返回了nil而我没检查,整个请求链路炸了,凌晨三点被oncall电话叫醒的那种体验——真的,一次就够你记一辈子。所以后来我写什么都加nil check,加到手软。但那篇文章让我意识到,这种条件反射式的"以防万一",本质上是在用战术上的勤奋掩盖战略上的懒惰。
该怎么做?你该做的不是到处加nil check,而是在设计层面就想清楚:这个东西到底能不能为nil?如果不能,那就用构造函数保证它不为nil,用文档说明它不为nil,用测试验证它不为nil。如果你发现自己在同一个对象的不同方法里反复检查同一个字段是不是nil——那说明你的设计有问题,不是你的检查不够多。
有人可能会问:那如果真的有可能为nil呢?比如一个可选参数,或者一个延迟初始化的字段?那就明确标注出来啊。用注释、用类型别名、用Option pattern——用什么手段都行,关键是让"可为nil"变成一个有意识的、可见的设计决策,而不是一个到处弥散的、隐式的恐惧。防御性编程不等于到处加nil检查,这就好比你不能说家里每个房间都放一个灭火器就叫消防安全——你得知道哪里真的会着火。
唉,说到这里我自己也有点惭愧。我回去翻自己半年前写的代码,有些地方确实是在"以防万一"的心态下加了一堆nil判断,读完之后连我自己都搞不清楚那个字段到底有没有可能为nil。这种代码不是防御性的,是迷惑性的。它给你一种虚假的安全感,同时把真正的风险藏在了一堆噪音里。你猜怎么着?后来我把那些多余的nil check全删了,跑了完整的测试套件,一条都没挂。那些检查从一开始就不需要存在。

Konrad的建议其实很朴素:把nil check放在它真正应该在的地方——也就是nil值真正有可能出现的地方。如果你的函数签名暗示了一个参数不应该为nil,那就不要在使用它之前做nil检查,而是在入口处做一次断言或者直接在文档里写清楚。如果你的类型在设计上就不应该处于零值可用的状态,那就确保它永远不会被零值构造出来。代码的可读性不是靠多写几行if x != nil来提高的,而是靠让每一行nil check都言之有物来实现的。
我最终把那个PR打回去了,附了一条评论:请告诉我哪些nil是真实存在的威胁,哪些只是你的焦虑。他第二天回了我一个很长的消息,说他自己也搞不清楚。说实话,这才是真正的问题。当你写nil check的时候你不是在解决一个已知的风险,你是在回应一种模糊的不安——而这种不安应该用设计去消除,不是用if去安抚。
关于维基框架
维基框架(Wiki Framework)是一套面向复杂业务场景的轻量级开发框架,支持多语言、多协议、多部署形态。适用于企业级应用开发、微服务架构、云原生部署等场景。
- 官网:https://framewiki.com
- Gitee:https://gitee.com/wiki-framework
- GitHub:https://github.com/wiki-framework
- 示例项目:https://gitee.com/cdkjframework/framewiki-example
- 📄 许可证:MulanPSL-2.0(木兰宽松许可证,第2版)