那行导致事故的代码,是我点的 Approve。
另外三位资深工程师也点了。
静态分析器点了。
Fuzzer 点了。
所有我们信任、专门用来抓这种 Bug 的工具,全都点了。
结果还是炸了。
一个 lambda 捕获了一个引用。
这个引用活得比它的作用域还久。
在某个层层嵌套的回调链里,我们开始读取一块已经不属于我们的内存。
监控系统在 19 分钟 之后才发现异常。
在交易系统里,19 分钟非常、非常长。
找到根因之后,我在停车场坐了半个小时。
不是因为愤怒。
而是因为我真的不知道——我们还能做什么更多的事。
所有最佳实践都做了。
现代 C++ 特性全用上了。
我们也很小心。
但这并不重要。
六个月后,我们用 Rust 重写了系统。
然后性能测试的结果,直接把我的世界观打碎了。
没人告诉你的生产事故真相
最糟糕的不是 Bug。
是事后的安静。
事故发生后,Slack 突然安静下来。
没人想留下任何一句可能会被写进复盘文档的话。
我看着在线状态一个个消失——
大家突然都“有事要忙”。
我留下了。
花了九个小时,顺着回调链一层层追指针,
在我们为了“让 C++ 看起来更安全”而构建的抽象里,
追踪所有权是如何悄悄溜走的。
Bug 藏在一个异步 handler 里。
外层作用域已经结束。
内层 lambda 还握着一个早就不存在的引用。
六行代码。
我们所有工具,全都看不见。
那一刻我才意识到一件很不舒服的事:
我们不是在 C++ 上失败。 我们是在 C++ 上成功了。
当一门语言允许你持有一个“幽灵引用”,
这就是成功的样子。
我们一直在骗自己的那个谎言
我们真的什么都做对了。
- 智能指针到处都是
- RAII 干净得可以裱起来
- 每次提交都跑静态分析
- Code review 动不动就三天,只因为“内存安全不能妥协”
我们曾经是那种会嘲笑“内存 Bug 公司”的团队。
我甚至在 2023 年做过一次分享,主题是:
安全是纪律问题,不是语言问题。
台下掌声还不错。
那时候我对自己感觉很好。
那些 PPT 我现在还留着。
偶尔打开看看,提醒自己什么叫自信的无知。
真相是这样的:
我们一直在防御一个永远不睡觉的敌人。
每一个 C++ 函数,都是一次与未定义行为的无声谈判。
你可以赢一千次。
你只需要输一次。
我们输了那一次。
代价是 230 万美元,
以及三位工程师决定“到此为止”。
所谓的“安全税”,到底是谁在交
大家一直说 Rust 慢。
说你要为安全付出性能代价。
说 borrow checker 不免费。
这是我们重写撮合引擎后的真实数据:
p99 延迟(微秒)
C++(之前) ████████████████████████ 47.2 μs
Rust(之后) ███████████████████ 38.1 μs
↓
快了 19.3%
我花了两周时间试图找 benchmark 的问题。
没有问题。
Rust 更快,是因为我们不再恐惧地写代码。
- 不再给永远不会是 null 的指针写防御判断
- 不再因为所有权不清晰而做多余拷贝
- 不再在运行时验证编译器早就能证明的事实
// 旧 C++(真实生产代码)
void process(Order* o) {
if (!o) return; // 理论上永远不为空,但谁敢信
auto copy = *o; // 所有权不清晰,先拷贝一份
if (copy.qty <= 0) return;
// 真正逻辑从这里才开始
}
// Rust 版本
fn process(o: &Order) {
// 编译器保证引用有效
// 从第一行就开始干正事
}
安全税确实存在。
只是我们之前,一直把钱交给了错误的语言。
那些离开的工程师
宣布重写的那周,Marcus 给整个工程团队发了一封邮件。
标题是:
C++ IS NOT THE PROBLEM
十四段文字。
十五年的经验。
一封为自己整个职业生涯辩护的长信。
他在周四辞职了。
我不怪他。
当编译器开始比你更擅长你的工作时,
人心里真的会断点什么。
你花十年培养的直觉,
突然变成可以忽略的警告。
那封邮件我留着。
偶尔会重读一遍,提醒自己:
“正确”和“善良”,不是一回事。
那个让我失眠的部分
上个月,一个初级工程师加入了团队。
23 岁。
8 个月 Rust 经验。
她第二周就提交了生产代码。
默认就是内存安全的代码。
放在 C++ 时代,
我至少会拉三个资深工程师盯着。
她不知道什么是悬空指针。
从没花九个小时追 use-after-free。
看到裸指针也不会条件反射地紧张。
因为她从没被它们伤过。
我有点嫉妒她。
也真心庆幸:
她不必像我一样学这些东西。
老实说一句
你该不该把 C++ 项目重写成 Rust?
我不会说“可能不用”。
我也不打算再保持理性。
我现在真正相信的是:
你留在 C++ 的每一天,
都在选择继续支付安全税。
静态分析器、代码评审、防御式编程、心理负担。
你用工程师的时间、注意力和精力在交这笔税。
在经历了这一切后,我开始在 云栈社区 的 Rust 和 C/C++ 板块更活跃地参与讨论。看到更多关于系统语言演进和安全性的思考,让我确信当初的选择并非孤例。