〇、先承认一个尴尬的事实
两年前我对 Rust 的态度是:一门为了让你在编译期就崩溃、从而避免你在运行期崩溃的语言。学了三天,写了个 CLI,被 borrow checker 教育了十七次,默默关掉了编辑器——典型的"从入门到放弃"。
今年我改主意了。不是因为 Rust 变简单了(它没有),而是因为我写代码的方式变了。
现在我一天能让 AI 生成两千行代码,但我一天最多认真 review 两百行。剩下的一千八百行,谁来把关?
这篇文章想聊的就是:当代码的生产成本趋近于零时,代码的验证成本就变成了唯一的瓶颈。而 Rust,恰好是目前把"验证"这件事外包给机器做得最彻底的主流语言。
一、瓶颈转移:从"写不出来"到"不敢合并"
传统开发的时间分布是这样的:想清楚 → 写出来 → 调通 → 上线。写代码本身占大头。
AI 辅助开发之后,这条曲线被压扁了,但压扁的只有中间那一段:
慢"] --> A3["调试"] --> A4["Review
人看人写的"] --> A5["上线"] end subgraph B["AI 辅助开发"] B1["设计"] --> B2["生成代码
快到离谱"] --> B3["验证
新瓶颈"] --> B4["Review
人看机器写的"] --> B5["上线"] end B3 -.->|"看不完 / 看不懂 / 不敢改"| B3
问题在于,人类 review 机器代码,和人类 review 人类代码,认知负担完全不同。
同事写的代码,你知道他的习惯、他的思路、他昨天踩过什么坑,你能"猜"到哪里危险。AI 写的代码,平均质量往往不差,语法漂亮、命名规范、注释齐全——但它出错的地方是随机的,而且常常出在"看起来非常正常"的地方。
我在支付系统里遇到过最惊悚的一类:AI 生成的并发代码,逻辑完全正确,压测也过了,只在特定的线程交错下才丢一笔账。这种东西靠肉眼 review 是抓不到的,靠测试也很难稳定复现。
所以真正的问题不是"AI 会不会写错",而是:错了以后,谁在什么时候能发现?
二、编译器是唯一愿意 7x24 陪 AI 加班的 Reviewer
Rust 最被低估的价值,不是"内存安全",而是它把大量运行时才能暴露的问题,搬到了编译期。
同一个并发 bug,在 Python/Java 里是一个"运行几百万次可能遇到一次"的概率事件,在 Rust 里是一个"你今天下午下班前必须解决的编译错误"。
举个最朴素的例子。下面这段 Python,AI 写得出来,你也 review 不出问题:
class Counter:
def __init__(self):
self.value = 0
def incr(self):
self.value += 1 # 多线程下这行是三个操作同样的意图在 Rust 里,你根本没机会写错:
struct Counter { value: i32 }
// 试图在多线程里共享 &mut Counter —— 编译不过
// 编译器会直接告诉你:要么加锁,要么用原子类型
use std::sync::atomic::{AtomicI32, Ordering};
struct Counter { value: AtomicI32 }
impl Counter {
fn incr(&self) {
self.value.fetch_add(1, Ordering::Relaxed);
}
}Send / Sync 这两个 trait 的存在,意味着"这个类型能不能跨线程"是一个类型系统里的事实,而不是一句写在 Wiki 上、三个月后没人记得的注释。
同样的还有:
Option<T>让"这里可能是空"变成必须显式处理的分支,而不是凌晨三点的 NPE 告警;Result<T, E>让"这里可能失败"无法被静默吞掉(#[must_use]会追着你骂);- 所有权让"这块内存归谁"成为编译期就确定的契约,而不是一个靠团队默契维持的口头约定。
而这一切,对 AI 来说恰好是最理想的反馈信号:
而非"内存与并发的正确性"
这一点非常关键:编译器的报错是确定性的、可复现的、结构化的(cargo check --message-format=json 直接吐 JSON)。Agent 可以在没有人类参与的情况下,拿到高质量的负反馈并自己收敛。
而在动态语言里,agent 的反馈来源是什么?是测试。测试覆盖不到的地方,它就是在裸奔——而且它自己写的测试,恰好最容易覆盖它自己想到的那些情况。
一句话总结:在 AI 写代码的时代,类型系统不是负担,是免费的、不会累的、不讲情面的 code reviewer。
三、顺便说一句:AI 基础设施本来就在被 Rust 悄悄重写
如果你只用 Python 调 API,可能没注意到一件事:你 pip install 下来的那些东西,底下越来越多是 Rust。
Qdrant / LanceDB"] R3["数据处理
Polars / Arrow"] R4["工具链
ruff / uv"] R5["推理与运行时
candle / burn"] end subgraph L4["系统层"] S1["CUDA / 内核 / 虚拟化"] end L1 --> L2 --> L3 --> L4
为什么是 Rust 而不是继续用 C++?我的理解有三条:
第一,尾延迟。 推理服务的痛点从来不是平均延迟,是 P99。有 GC 的语言在高吞吐下总会有那么几毫秒到几百毫秒的停顿,而 Rust 没有 GC,延迟曲线是可预测的。做过支付网关的人对这件事有生理性的敏感——我们的 SLA 从来不写平均值。
第二,FFI 成本极低。 Rust 可以编译成 C ABI 的动态库,PyO3 让 Python 侧几乎零感知。这意味着"上层 Python 保持灵活,下层 Rust 保证性能与安全"是一个可以渐进落地的方案,而不是要求团队全面重写。
第三,交付形态干净。 单个静态二进制,没有 runtime、没有 JVM、没有 node_modules、没有 glibc 版本地狱。在容器里,这意味着几 MB 的镜像和秒级冷启动——对需要弹性扩缩容的推理网关来说,这是真金白银。
顺带一提,uv 和 ruff 这两个 Rust 写的 Python 工具,把 Python 生态的体验提升了一个量级。这件事本身就挺有讽刺意味的:拯救 Python 开发体验的,是 Rust。
四、Agent 需要沙箱,而沙箱需要一门没有 UB 的语言
这是我觉得最被忽视的一点。
当我们开始让 AI 执行代码——不只是生成,而是真的跑起来、连数据库、调 API、写文件——安全模型就从"人类不会故意作恶"变成了"这段代码是一个概率模型吐出来的,我不知道它会干什么"。
传统的信任边界完全失效了。你需要的是能力控制(capability-based security):这个 agent 只能访问这三个目录、只能连这两个域名、最多用 512MB 内存、最多跑 30 秒。
不可信"] CODE --> SB subgraph SB["WASM 沙箱 —— wasmtime / WasmEdge"] direction TB EX["执行"] CAP["能力白名单
文件 / 网络 / 时间 / 内存"] EX --- CAP end SB -->|"仅允许的系统调用"| HOST["宿主资源"] SB -.->|"越权即拒绝"| DENY["拒绝并记录"] style CODE fill:#ffe0e0,stroke:#c00 style DENY fill:#ffe0e0,stroke:#c00
而 WASM 生态的事实标准工具链,基本都是 Rust 写的,Rust 也是编译到 WASM 体验最好的语言。再往下一层,Firecracker 这类微虚拟机同样是 Rust。
逻辑其实很朴素:你用来隔离不可信代码的那层东西,自己绝对不能有内存安全漏洞。用一门存在未定义行为的语言去写沙箱,约等于用纸糊防火墙。
五、从入门到放弃环节:Rust 的代价,一条都没少
吹完了,得说点实话,不然这文章就成软文了。
编译慢。 大型项目改一行等半分钟是常态。在"改代码—看结果"这个循环上,Rust 的体验比 Go 差一个数量级。而 AI 辅助开发恰恰高度依赖快速迭代循环。这是实打实的矛盾。
AI 写 Rust 的水平,不如它写 Python。 训练语料的体量摆在那儿。LLM 写 Rust 时,幻觉出已经废弃的 API、写出过不了 borrow checker 的代码、在 async 生命周期上绕不出来,都是日常。区别在于——这些错误编译器会当场拦下来,而不是上线之后由用户帮你发现。所以它写得更费劲,但你收到的东西更可靠。这笔账怎么算,看你项目的容错空间。
async 生态有割裂感。 运行时选型、Send 约束、生命周期在异步里的传染性,这些是真的劝退。
学习曲线是真陡。 不要跟新人说"Rust 其实不难",那是骗人的。它只是把痛苦从"上线之后"提前到了"编译之前",总量不一定变少,但发生的时间点对你非常有利。
所以我的建议很保守:不要用 Rust 写 CRUD,不要用 Rust 写一次性脚本,不要因为"未来趋势"就把整个团队的技术栈掀了。
六、我的实用结论:分层,而不是站队
我现在的做法是按"这段代码错了会怎样"来分层:
| 层次 | 选型 | 理由 |
|---|---|---|
| 实验、调研、Prompt 编排 | Python | 迭代速度压倒一切,错了重跑就行 |
| 业务逻辑、常规服务 | Java / Go | 生态成熟,团队上手快,AI 也写得顺 |
| 性能热路径(tokenize、向量检索、序列化) | Rust | 无 GC 停顿,P99 可控,PyO3 平滑接入 |
| 安全边界(沙箱、网关、加解密、账务核心) | Rust | 这里的 bug 不是 bug,是事故 |
判断标准其实只有一条:
这段代码出错的成本,是"重跑一次",还是"上报监管"?
前者用最快的语言写,后者用最严的语言写。
尾声
我一直觉得,Rust 在 AI 时代的位置有点像会计里的复式记账法:它逼着你在录入的那一刻就保证两边平衡,过程繁琐、反人性、学起来痛苦,但它让"错误"这件事变得无处可藏。
当代码由人一行行敲出来时,这种严格显得像洁癖。当代码由模型批量生成、由 agent 自动执行时,这种严格就是你手上为数不多、还能真正信任的东西。
AI 让写代码变便宜了,但它没有让承担后果变便宜。
所以我又把那本 Rust 的书翻出来了。这次希望不要再放弃了。