一个写了 5 年 Python 的人去学 Rust,第一周差点把键盘砸了

3 阅读7分钟

说在前面:我不是那种"学新语言跟喝水一样"的大佬。我写 Python 写了快 5 年,FastAPI、Django、数据处理、爬虫,基本上 Python 能干的活我都干过。2026 年初,团队有个性能敏感的服务要重写,leader 说"要不试试 Rust",我想着反正有 Claude Code 帮忙写,能有多难?

结果第一周我就被所有权系统教做人了。

这篇文章不是 Rust 教程(官方 The Book 比我写得好一万倍),而是一个 Python 程序员学 Rust 时真实的心路历程和踩坑记录。如果你也是动态语言背景,想试试 Rust,希望能帮你少走点弯路。

为什么要学 Rust

先说动机,不然显得我没事找事。

我们有一个日志解析服务,Python 写的,每天处理大概 2000 万条日志。之前还撑得住,但最近数据量翻了一倍,Python 的 GIL 加上 JSON 解析的开销,CPU 直接打满。用了 multiprocessing 开多进程,内存又爆了。

当时摆在面前的选择:

  1. Go — 团队有人会,上手快
  2. Rust — 没人会,但性能天花板高
  3. C++ — 2026 年了,真没必要给自己找罪受

最后选了 Rust,原因很简单:这个服务一旦写完基本不怎么改,追求的是极致性能和内存安全,Rust 刚好对口。Go 的 GC 停顿在我们这个场景下也会有影响。

Python 思维 vs Rust 思维:根本不是一个物种

这是我学 Rust 最大的感受。不是语法难,是思维方式完全不同。

graph TD
 A[Python 思维] --> B[变量随便赋值]
 A --> C[垃圾回收帮你管内存]
 A --> D[运行时才报错]
 A --> E[一切皆引用]
 
 F[Rust 思维] --> G[所有权必须明确]
 F --> H[你自己管内存,编译器帮你检查]
 F --> I[编译时就把错误拦住]
 F --> J[移动语义是默认行为]

第一个坑:变量"用了就没了"

Python 里你不会遇到这种问题:

data = [1, 2, 3]
process(data)
print(data) # 完全没问题,data 还在

Rust 里,同样的逻辑直接报错:

fn main() {
 let data = vec![1, 2, 3];
 process(data);
 println!("{:?}", data); // 编译错误!data 已经被 move 了
}

fn process(v: Vec<i32>) {
 println!("processing: {:?}", v);
}

编译器会告诉你:value used here after move

我第一次看到这个错误的时候,真的懵了。什么叫"move"?我就是传了个参数啊?

Rust 的所有权规则就三条:每个值有且只有一个所有者;值在任一时刻只能有一个可变引用,或多个不可变引用;所有者离开作用域,值被自动释放。

Python 程序员可以这么理解:Rust 里把变量传给函数,就像你把房子钥匙给了别人,自己就没钥匙了。想保留?要么给别人一把备用钥匙(引用 &),要么复制一套房(.clone())。

修复后的代码:

fn main() {
 let data = vec![1, 2, 3];
 process(&data); // 借用,不转移所有权
 println!("{:?}", data); // OK,data 还是你的
}

fn process(v: &Vec<i32>) {
 println!("processing: {:?}", v);
}

第二个坑:生命周期标注

这是我差点放弃 Rust 的地方。

fn longest(x: &str, y: &str) -> &str {
 if x.len() > y.len() { x } else { y }
}

编译器说:missing lifetime specifier。我心想,你自己推导不出来吗?两个参数都是引用,返回其中一个,这有什么难理解的?

但编译器确实推导不出来——它不知道返回值的生命周期跟 x 走还是跟 y 走。得显式告诉它:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
 if x.len() > y.len() { x } else { y }
}

这个 'a 就是生命周期标注,意思是"返回值至少活得跟 xy 中较短的那个一样久"。

说实话,我花了整整两天才真正理解这个东西。后来总结了一个规律:只要函数签名里有引用输入和引用输出,大概率要标生命周期。编译器报错了就加,加到不报错为止——虽然有点暴力,但前期真的管用。

让我"哦!"的那些时刻

学 Rust 不全是痛苦,有些设计确实让我觉得对路。

模式匹配 + 枚举

Python 里处理可能为空的值:

result = get_user(user_id)
if result is not None:
 print(result.name)
else:
 print("not found")

问题是忘了判空,Python 不会提醒你,直到运行时炸一个 AttributeError

Rust 的 Option 枚举从根上解决了这个问题:

fn get_user(id: u32) -> Option<User> {
 // ...
}

match get_user(42) {
 Some(user) => println!("{}", user.name),
 None => println!("not found"),
}

漏掉 None 分支,编译器直接报错。空指针异常在 Rust 里基本不存在。写了 5 年 Python 被 NoneType has no attribute xxx 折磨过无数次,看到这个设计的时候真的有被感动到。

错误处理:Result + ? 运算符

Python 的 try-except 有个问题:你不知道一个函数到底会抛什么异常,除非去翻文档(而且文档经常不全)。

use std::fs;
use std::io;

fn read_config() -> Result<String, io::Error> {
 let content = fs::read_to_string("config.toml")?;
 Ok(content)
}

这个 ? 是语法糖,意思是"如果出错了就提前返回错误,没出错就把值拿出来"。函数签名里的 Result<String, io::Error> 明确告诉你:这个函数可能失败,失败原因是 IO 错误。类型系统强制你处理错误,而不是像 Python 那样先跑起来再说。

一周下来的真实感受

爽的:

  • 编译通过基本等于程序能跑,运行时惊喜极少
  • cargo 是我用过最顺手的包管理工具,对比 pip 和 poetry 那个混乱局面,差距不是一点半点
  • 性能是真的猛,日志解析的核心逻辑用 Rust 重写了一小段,处理速度是 Python 的 40 倍左右,内存占用只有十分之一

抓狂的:

  • 编译速度太慢,中等项目 cargo build 首次编译要两三分钟,增量也要十几秒。Python 根本没有"编译"这个概念,保存就能跑
  • 所有权加生命周期的学习曲线确实陡,前三天基本在跟编译器吵架
  • 字符串类型有 String&str&StringCow<str>……到现在有时候还会搞混
  • 生态跟 Python 的 PyPI 比还是差很多,数据处理和 ML 领域尤其明显

给 Python 程序员的几条建议

1. 先把 The Book 前 10 章老老实实看完

别上来就抄 AI 生成的代码跑。不理解所有权,AI 给你的代码你改都不会改。我一开始让 Claude Code 帮我写,生成的代码能跑,但完全看不懂为什么要加 &、为什么要 .clone(),出了问题两眼一抹黑。

2. 别急着用高级特性

Trait、泛型、生命周期标注、宏——前一周都不用管。先用最笨的方式写:到处 .clone(),用 String 而不是 &str,能 unwrap() 就先 unwrap()。先跑起来,再优化。

3. 善用编译器的错误提示

Rust 编译器的错误信息是我见过最友好的,不仅告诉你哪错了,还告诉你怎么改:

help: consider borrowing here: `&data`

认真读每一条 error 和 help,比查 Stack Overflow 快多了。

4. 找一个实际项目练手

推荐写个 CLI 工具。用 clap 做参数解析,serde 做 JSON 处理,tokio 做异步 IO,Rust 的核心概念基本都能覆盖到。

最终结果

那个日志解析服务,我花了大概三周写完 Rust 版本(后两周顺畅多了)。上线后的数据:

  • CPU 使用率从 85% 降到 12%
  • 内存从 4GB 降到 400MB
  • 处理延迟从 P99 800ms 降到 P99 20ms
  • 两个月了,没崩过一次

这个结果说实话有点震撼。开发速度确实比 Python 慢不少,但对于"写一次跑很久"的基础服务,投入产出比真的高。

我现在的策略是:快速迭代、业务逻辑用 Python;性能敏感、长期运行的底层服务用 Rust。两者不冲突,甚至可以通过 PyO3 互相调用。

Rust 填补了一个 Python 填不了的坑。如果你也被 GIL 和内存问题折磨过,值得试试。前三天会很痛苦,挺过去就好了。