Rust 编译器报错 'cannot borrow as mutable'?把 s.clear() 当成“撕书”你就懂了

写 Rust 最让人头秃的时候,大概就是觉得自己逻辑完美,编译器却甩给你一串红字的 cannot borrow as mutable。
你只是想用 s.clear() 清空个字符串而已,招谁惹谁了?这时候的 Rust 就像个死板的图书管理员,死活不让你动手。
但这真不是在针对你。我们换个角度,用**“正在读的书能不能撕”**这个例子,来看看这报错到底在保护什么。
案发现场
假设你想找出字符串的第一个单词,用完之后把字符串清空省点内存。代码大概长这样:
fn main() {
let mut s = String::from("hello world");
// 1. 借出一个不可变引用(word 是 s 的一个切片)
let word = first_word(&s);
// 2. 试图清空字符串(需要可变借用)
s.clear(); // ❌ 报错!
// 3. 再次使用那个不可变引用
println!("the first word is: {}", word);
}
fn first_word(s: &String) -> &str {
// ...省略具体实现,返回 s 的一个切片
&s[0..5]
}
编译器马上跳出来警告:
cannot borrow 's' as mutable because it is also borrowed as immutable
你可能会嘀咕:“我就清空一下 s,跟 word 有什么关系?”
图书馆里不准撕书
把 s 想象成图书馆里的一本孤本。
不可变借用 (
&s):这就是在馆内阅读。图书馆允许多个人同时阅读(多个人持有不可变引用),大家围在桌子旁一起看,没问题。这时候,word就是你抄在手心的小抄(比如:这本书的第1页到第5页)。可变借用 (
&mut s):这就是s.clear()想干的事——修改内容,甚至把书撕成白纸。
为什么冲突?
想象一下,你正拿着小抄(word)读得津津有味,突然冲过来一个人要撕书(s.clear())。
如果他真撕了,你手里的小抄指向的就不是书页,而是空气。这时候再去读(println!),程序要么崩,要么读出一堆乱码。
Rust 的铁律很简单:
有人读的时候,谁也别想改。 (Readers exclude Writers)

怎么解决?
既然是“读写冲突”,解决办法就两个:要么读完再改,要么各玩各的。
1. 读完再改(调整位置)
这是最简单的。既然 word 还要用(在 println! 里),那就等用完再说。只要确保 s.clear() 在 word 谢幕之后就行。
fn main() {
let mut s = String::from("hello world");
// 作用域开始:word 借用了 s
let word = first_word(&s);
// 在这里先使用 word,用完之后,Rust 的 NLL (Non-Lexical Lifetimes) 机制
// 会判定 word 的生命周期结束了
println!("the first word is: {}", word);
// 此时 s 身上已经没有借用了,可以随意修改
s.clear(); // ✅ 编译通过!
}
类比一下:你看完书,笔记做完了,人也走了。这时候管理员再来撕书(清理),谁也不会受影响。
2. 自己复印一份(Clone)
如果你非要在 s.clear() 之后还能使用那个单词,那就别借书看了,自己复印一份带走。
fn main() {
let mut s = String::from("hello world");
// 不借用 s 的引用,而是把切片转换成全新的 String
let word = first_word(&s).to_string();
s.clear(); // s 被清空了
// word 是自己复印的副本,跟 s 没关系了,随便用
println!("the first word is: {}", word); // ✅ 编译通过!
}

代价:to_string() 或 clone() 得复印数据,稍微多费点内存(买书比借书贵),但这俩变量从此就没瓜葛了。
常见坑
隐式借用
有时候你没写 &,但有些方法悄悄借用了。
let len = s.len(); // len() 只是读取,短暂不可变借用,瞬间结束
s.clear(); // 没问题
Debug 打印
新手常犯的毛病:为了调试看变量,结果不小心把生命周期拖长了。
let word = first_word(&s);
s.clear();
// 这一行原本是为了调试,结果导致了上面的 clear 报错
// 删掉这行,或者把它移到 clear 之前,代码就能跑了
dbg!(word);
总结一下
记句口诀,Rust 借用不再难:
共享不可变,可变不共享。 要想撕书,等读者走光。
救命小抄
遇到 cannot borrow as mutable:
- 查引用:这行之前,谁借了
&s? - 看结尾:那个借用(比如
word)最后一次在哪用的? - 挪位置:能把修改操作(
s.clear())挪到最后一次使用后面吗? - 大招:实在不行,
.clone()吧。
觉得这篇文章有用吗?
- 点赞:如果觉得“撕书”这个比喻让你秒懂,点个赞支持一下!
- 转发:分享给身边正在和 Rust 编译器搏斗的朋友。
- 关注:关注梦兽编程,每天一个生活案例解锁技术难题。
- 留言:你还遇到过哪些让你抓狂的 Rust 报错?欢迎评论区吐槽,下一篇没准就是为你写的!
你的支持是我持续创作的最大动力!