news 2026/3/26 12:55:49

让编译器成为你的副驾驶:告别“防御性编程”,拥抱“类型驱动开发”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
让编译器成为你的副驾驶:告别“防御性编程”,拥抱“类型驱动开发”

大家好,我是Tony Bai。

“半夜被值班的运维同事叫醒,发现生产环境崩了,原因是一个深藏在业务逻辑里的nil指针异常。”

这个场景,对于每个后端开发者来说都是挥之不去的噩梦。事后复盘时,我们往往会懊恼:“为什么这里没加if != nil判断?”然后,我们在代码里撒上一把防御性检查的“盐”,祈祷下次好运。

但这真的是解决之道吗?

最近,Daniel Beskin 的一篇深度好文《The Compiler Is Your Best Friend, Stop Lying to It》(编译器是你最好的朋友,别再对它撒谎了),为我们提供了一个全新的视角:这些运行时崩溃,本质上是因为我们在编译时对编译器撒了谎。

我们告诉编译器“这是一个字符串”,但实际上它可能是nil;我们告诉编译器“这个函数返回一个整数”,但实际上它可能抛出一个panic。当我们停止撒谎,开始用类型系统表达真实意图时,编译器将从一个“报错机器”,变成我们最强大的“安全副驾驶”。

我们对编译器撒过的“谎”

在 Go 语言的日常开发中,我们常常为了“方便”而向编译器撒谎,埋下了日后爆炸的地雷。

谎言一:隐形的nil

当我们定义func Process(u *User)时,我们告诉编译器:“给我一个 User,我处理它。” 但在 Go 中,指针可以是nil

  • 谎言:我承诺会处理一个 User。

  • 真相:我可能会收到一个nil,然后炸掉。

  • 后果:为了弥补这个谎言,我们需要在函数内部写无数的if u == nil防御性代码。一旦遗漏,就是生产事故。

谎言二:盲目的类型断言与any

当我们使用interface{}(或any) 时,我们实际上是在对编译器说:“别管这个,我知道我在做什么。”

  • 谎言:这个any类型的变量,其实是一个int

  • 真相:它可能是一个string,或者nil

  • 后果:运行时的panic: interface conversion: interface {} is string, not int

谎言三:隐藏的副作用与 Panic

当我们看到一个函数签名func Parse(s string) int时,编译器认为它是一个将字符串映射为整数的函数。

  • 谎言:这是一个纯粹的转换函数。

  • 真相:如果字符串格式不对,我会直接panic,中断整个 goroutine。

  • 后果:调用者无法通过函数签名预知风险,导致程序在边缘情况下意外崩溃。

停止撒谎,开启“对话”

如何重建与编译器的信任关系?答案是:将运行时的检查,提前到编译时的类型定义中。

策略一:让非法状态无法表示

这是消除nil和无效数据的终极心法。

  • 场景:一个配置项Port,如果是 0 表示随机端口,如果是正数表示指定端口。

  • 糟糕的设计Port int。你必须在代码各处检查Port < 0的情况,并且含义模糊。

  • 诚实的设计

    type Port int // 使用构造函数来保证 Port 的合法性 func NewPort(p int) (Port, error) { if p < 0 || p > 65535 { return 0, fmt.Errorf("invalid port") } return Port(p), nil }
    一旦你通过NewPort拥有了一个Port类型的值,编译器就为你担保:它一定是一个合法的端口号。你后续不再需要防御性检查(未通过NewPort获得的除外)。

策略二:用类型区分概念

  • 场景:用户 ID 和 订单 ID 都是int64

  • 糟糕的设计func GetOrder(userID, orderID int64)。调用者很容易把两个 ID 传反,而编译器毫无察觉。

  • 诚实的设计

    type UserID int64 type OrderID int64 func GetOrder(uid UserID, oid OrderID) { ... }
    现在,如果你试图把UserID传给OrderID,编译器会直接报错。这不是繁琐,这是编译器在帮你 Review 代码

策略三:显式的可空性

虽然 Go 没有 Rust 的Option<T>,但我们可以利用指针的语义来诚实地表达“可能不存在”。

  • 场景:更新用户信息,只更新非空字段。

  • 诚实的设计

    type UpdateUserRequest struct { Name *string // nil 表示不更新,非 nil 表示更新为新值 Age *int }
    这里,指针不再是“可能导致崩溃的引用”,而是“可选值”的显式类型标记。这让代码的意图对编译器和人类都一目了然。

编译器是你的朋友,不是敌人

很多时候,我们觉得编译器很烦人:它阻止我们快速写出“能跑”的代码,强迫我们处理每一个err,纠结于类型转换。

但 Daniel Beskin 提醒我们:编译器是你唯一一个会不厌其烦地帮你检查每一个细节、永远不会疲倦、永远不会因为“差不多就行”而放过 Bug 的队友。

当你觉得编译器在“阻碍”你时,停下来想一想:是不是我在试图对它撒谎?

  • 如果类型不匹配,是不是我的数据模型设计得不够清晰?

  • 如果错误处理太繁琐,是不是因为我试图把不确定的状态传递得太远?

小结:睡个好觉的秘诀

“防御性编程”是一种补救措施,它假设代码是脆弱的。而“类型驱动开发”是一种预防措施,它利用编译器构建坚固的堡垒。

当我们开始尊重类型,停止用any和隐式约定来糊弄编译器时,我们获得的回报是巨大的:

  • 重构时的自信:修改一个类型,编译器会告诉你所有需要调整的地方。

  • 更少的测试:你不需要测试“端口号是否为负数”,因为类型系统保证了它不可能为负。

  • 更安稳的睡眠:因为你知道,那些导致半夜崩溃的低级错误,早在你按下go build的那一刻,就被忠诚的编译器拦截在了门外。

资料链接:https://blog.daniel-beskin.com/2025-12-22-the-compiler-is-your-best-friend-stop-lying-to-it


你的“撒谎”时刻

读完这篇文章,你是否也意识到了自己曾在代码中对编译器撒过的“谎”?在你的项目中,有哪些因为类型定义不清而导致的“血案”?或者,你有哪些利用类型系统来规避 Bug 的独门绝技?

欢迎在评论区分享你的反思与心得!让我们一起学会“诚实”编程,睡个好觉。👇

如果这篇文章颠覆了你对编译器的认知,别忘了点个【赞】和【在看】,并转发给你的团队,一起提升代码的“诚实度”!


如果本文对你有所帮助,请帮忙点赞、推荐和转发

点击下面标题,干货!

- Go 编译器崩溃背后:一个 append 函数引发的语言规范修正案

- Go语言正在成为“老旧”生态的“新引擎”?从 FrankenPHP 和新版 TypeScript 编译器谈起

- Go类型系统:有何与众不同

- 告别懵圈:实战派Gopher的类型理论入门

- 告别 interface{} 模拟,Go 终于要有真正的 Union 类型了?

- Go 泛型再进化:移除类型参数的循环引用限制

- 告别字符串魔法:Go迎来类型化Struct Tag提案,编译期安全触手可及?


🔥 还在为“复制粘贴喂AI”而烦恼?我的新极客时间专栏《AI原生开发工作流实战》将带你:

  • 告别低效,重塑开发范式

  • 驾驭AI Agent(Claude Code),实现工作流自动化

  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码👇,开启你的AI原生开发之旅。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/21 16:43:12

League Director:英雄联盟专业级回放视频制作全攻略

League Director&#xff1a;英雄联盟专业级回放视频制作全攻略 【免费下载链接】leaguedirector League Director is a tool for staging and recording videos from League of Legends replays 项目地址: https://gitcode.com/gh_mirrors/le/leaguedirector 还在为无法…

作者头像 李华
网站建设 2026/3/18 15:14:50

Wallpaper Engine创意工坊下载器:告别Steam束缚的终极解决方案

Wallpaper Engine创意工坊下载器&#xff1a;告别Steam束缚的终极解决方案 【免费下载链接】Wallpaper_Engine 一个便捷的创意工坊下载器 项目地址: https://gitcode.com/gh_mirrors/wa/Wallpaper_Engine 在动态壁纸爱好者群体中&#xff0c;Wallpaper Engine凭借其丰富…

作者头像 李华
网站建设 2026/3/26 7:39:23

存储为什么涨那么猛?

大家知道&#xff0c;我的圈子里面有一些头部的企业&#xff0c;有几个跟我关系还可以的&#xff0c;然后前几天跟一个做主板方案的朋友聊天&#xff0c;我本想问问去年企业的业绩如何&#xff0c;有没有达到年初的目标&#xff0c;也侧面想了解下我们嵌入式行业的情况&#xf…

作者头像 李华
网站建设 2026/3/26 2:33:25

Xbox成就解锁完整教程:免费工具轻松达成全成就目标

Xbox成就解锁完整教程&#xff1a;免费工具轻松达成全成就目标 【免费下载链接】Xbox-Achievement-Unlocker Achievement unlocker for xbox games (barely works but it does) 项目地址: https://gitcode.com/gh_mirrors/xb/Xbox-Achievement-Unlocker 还在为Xbox游戏中…

作者头像 李华
网站建设 2026/3/26 9:09:29

智能体记忆机制演进之路:从RAG到智能体记忆的演进

在学习AI智能体的记忆机制时&#xff0c;我发现自己被各种新术语搞得晕头转向。一开始是短期记忆和长期记忆&#xff0c;接着又出现了程序性记忆、情景记忆和语义记忆&#xff0c;让人更加困惑。但等等&#xff0c;语义记忆让我想起了一个熟悉的概念&#xff1a;检索增强生成&a…

作者头像 李华
网站建设 2026/3/25 10:22:06

软件工程原理与实践期末考试专项突破:深入解析“软件与软件危机”核心考点

软件工程原理与实践期末考试专项突破&#xff1a;深入解析“软件与软件危机”核心考点适用对象&#xff1a;计算机科学与技术、软件工程及相关专业本科生 考试重点&#xff1a;软件的本质特征、软件危机的成因与表现、软件工程的诞生背景及应对策略相关重点知识点总体预览 在《…

作者头像 李华