外观
雷区 2:E0502 借来读的时候,又想借去改
报错码:E0502
症状
rust
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("key".to_string(), 1);
let value = map.get("key");
map.insert("key2".to_string(), 2);
println!("{:?}", value);
}编译输出
text
error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
--> src/main.rs:8:5
|
7 | let value = map.get("key");
| --- immutable borrow occurs here
8 | map.insert("key2".to_string(), 2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
9 |
10| println!("{:?}", value);
| ----- immutable borrow later used here诊断
这是借用检查器最经典的报错,没有之一。第 3 章的规矩:一个值,要么同时有很多人"看"(&),要么一个人"改"(&mut),不能边看边改。
事故时间线:
- 第 7 行
map.get("key")→ 借来看 map,借条存在value里 - 第 8 行
map.insert(...)→ 想借去改 map - 冲突! 改的时候,
value手里的借条还活着(第 10 行还要用)
真实项目里的高频场景:读一个值、然后修改集合、再使用读到的值——比如"查字典→更新字典→打印查到的"、"遍历时删除"。
解药
解药一:复印读到的值(clone),提前结束借用——值小、复印不心疼时:
rust
fn main() {
let mut map = HashMap::new();
map.insert("key".to_string(), 1);
let value = map.get("key").cloned();
map.insert("key2".to_string(), 2);
println!("{:?}", value);
}get 返回 Option<&V>,.cloned() 变成 Option<V>——借条当场作废,后面随便改。
解药二:缩小借用的作用域——借用只活到它被用完为止:
rust
fn main() {
let mut map = HashMap::new();
map.insert("key".to_string(), 1);
let value;
{
value = map.get("key").cloned();
}
map.insert("key2".to_string(), 2);
println!("{:?}", value);
}解药三:先改,后读——调整顺序,让借用自然结束:
rust
fn main() {
let mut map = HashMap::new();
map.insert("key".to_string(), 1);
map.insert("key2".to_string(), 2);
let value = map.get("key");
println!("{:?}", value);
}解药一的变体:get_mut
如果你确实要"读到的那个位置,改它"——用 get_mut 拿到可变借条,一次搞定:
rust
if let Some(v) = map.get_mut("key") {
*v += 1;
}"边看边改同一个东西"不是罪,罪的是两条借条同时活着。get_mut 只有一条借条,合法。
预防
- 借条的寿命从"创建"算到"最后一次使用"(不是到作用域结束!)——
value如果后面不再用,编译器会自动提前结束借用 - **"查→改→再用查到的"**这个模式,先想清楚:查到的值要留多久?复印件(
cloned)通常是最省心的解药 - 看到 E0502,先找两条借条:
immutable borrow occurs here和mutable borrow occurs here各在哪,然后想"能不能让一条先下班"