外观
雷区 1:E0382 值已移动,你还用它
报错码:E0382
症状
rust
fn main() {
let s = String::from("hello");
let t = s;
println!("{}", s);
let _ = t;
}编译输出
text
error[E0382]: borrow of moved value: `s`
--> src/main.rs:4:20
|
2 | let s = String::from("hello");
| - move occurs because `s` has type `String`, which does not implement the `Copy` trait
3 | let t = s;
| - value moved here
4 | println!("{}", s);
| ^ value borrowed here after move
|
help: consider cloning the value if the performance cost is acceptable
|
3 | let t = s.clone();
| ++++++++(行号以你的实际为准。关键看三行:声明处、移动处、使用处。)
诊断
第 3 章的老朋友,但真实项目里它以各种姿势出现:
let t = s;把s的所有权交给了t(移动)- 第 4 行再碰
s→ 编译器大喊:"value moved here(这里搬走了)value borrowed here after move(这里还想用)"
报错的三行 --> 就是事故时间线:声明 → 移动 → 使用。编译器不是在刁难你,它在说:"第 3 行的移动,和第 4 行的使用,只能留一个。"
最常见的三种触发场景:
rust
// 场景 1:赋值移动
let t = s; // s 被搬走
println!("{}", s); // 再碰 s → 炸
// 场景 2:传参移动
fn consume(s: String) { ... }
consume(s); // s 被搬进函数
println!("{}", s); // 再碰 s → 炸
// 场景 3:结构体字段移动
let name = pet.name; // 把字段搬走
println!("{}", pet); // 再碰整个 pet → 炸解药
解药一:复印一份(clone)——你确实需要两个:
rust
fn main() {
let s = String::from("hello");
let t = s.clone();
println!("{}", s);
let _ = t;
}解药二:借用而不是搬走(&)——你只需要"看":
rust
fn main() {
let s = String::from("hello");
let t = &s;
println!("{}", s);
let _ = t;
}解药三:调整顺序——先用完,再移动:
rust
fn main() {
let s = String::from("hello");
println!("{}", s); // 先用
let t = s; // 再搬
let _ = t;
}预防
- 记住口诀:
String/Vec等"活"类型搬家(move),数字/布尔等"复印机"类型复制(Copy)——第 3 章的复印件理论,真实项目里每天都在用 - 报错先看时间线:哪一行搬走,哪一行使用,想清楚"我到底想留哪边"
.clone()不是万灵药:复印要花钱(内存和时间)。能借用就别复印,这是性能意识的第一课(第 9 章练习讲过"借来的东西别乱 clone")