
学 Rust 第一周:我是谁?我在哪?为什么我的代码又编译不过?
说实话,刚开始学 Rust 的时候,我被所有权系统折磨得够呛。
记得那天晚上,我写了这样一段代码:
fn main() {
let s = String::from("hello");
let t = s;
println!("{}", s);
}
我觉得很合理啊——把 s 赋值给 t,然后打印 s,有啥问题?
结果编译器给我泼了一盆冷水:
error[E0382]: borrow of moved value: `s`
--> src/main.rs:4:20
|
2 | let s = String::from("hello");
3 | let t = s;
| - value moved here
4 | println!("{}", s);
| ^ value borrowed here after move
我当时就想:这编译器是不是跟我过不去?
后来才明白,不是编译器有问题,是我没搞懂 Rust 的"规矩"。今天咱们就来搞懂这个让无数新手抓狂的所有权系统。
Rust 的所有权系统,说白了就三条规则:
听起来挺简单吧?但实际写代码的时候,坑可不少。
所有权就像你家的房子:
🏠 房子只能有一个房主(同一时间只能有一个所有者) 🔑 但钥匙可以借给别人(可以借用,但不能据为己有) 🗑️ 房主搬走了,房子就被清空(所有者离开作用域,值被 drop)
这样一想,是不是好理解多了?
在 Rust 里,赋值操作会转移所有权:
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权转移给 s2
这时候 s1 还能用吗?不能!
println!("{}", s1); // ❌ 编译错误!
编译器在说什么人话?
编译器:兄弟,
s1已经给别人了,你咋还用呢?
其他语言(比如 Python、Java)怎么处理的?
# Python
s1 = "hello"
s2 = s1 # 两个变量都指向同一个字符串
Python 用的是引用计数,Rust 用的是所有权系统。
Rust 的优势:
代价:
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权转移给 s2
// println!("{}", s1); // ❌ 错误!s1 已经无效了
println!("{}", s2); // ✅ 正确!s2 是新的所有者
}
运行结果:
hello
如果你想要一份副本,而不是转移所有权,怎么办?
用 .clone():
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝,s1 和 s2 都有独立的数据
println!("{}", s1); // ✅ s1 还能用
println!("{}", s2); // ✅ s2 也能用
}
运行结果:
hello
hello
有些类型实现了 Copy trait,赋值时自动复制:
fn main() {
let x = ; // i32 实现了 Copy
let y = x; // 自动复制,x 还能用
println!("{}", x); // ✅ 正确
println!("{}", y); // ✅ 正确
}
哪些类型实现了 Copy?
i32, u64 等)f32, f64)bool)char)String 为什么没有 Copy?
因为 String 管理着堆上的数据,不能简单地按位复制!
fn takes_ownership(s: String) {
println!("{}", s);
} // s 在这里被 drop
fn main() {
let s = String::from("hello");
takes_ownership(s); // s 的所有权转移给函数
// println!("{}", s); // ❌ 错误!s 已经无效了
}
编译器吐槽:
编译器:你把
s传给函数后,它就不属于你了!
解决方案:
// 方案 1:传递引用(借用)
fn takes_reference(s: &String) {
println!("{}", s);
}
// 方案 2:返回所有权
fn returns_ownership(s: String) -> String {
s
}
fn gives_ownership() -> String {
String::from("hello")
}
fn main() {
let s = gives_ownership(); // s 获得所有权
println!("{}", s); // ✅ 正确
}
这个没问题,但如果这样写:
fn wrong_way() -> String {
let s = String::from("hello");
s // 返回 s,所有权转移给调用者
}
fn main() {
let s = wrong_way();
println!("{}", s); // ✅ 正确
}
fn main() {
let s1 = String::from("hello");
let s2 = s1; // 转移所有权
println!("{}", s1); // ❌ 错误!
}
错误信息:
error[E0382]: borrow of moved value: `s1`
修复方法:
let s2 = s1.clone(); // 克隆一份
假设你要写一个函数,处理用户输入的字符串:
fn process_input(input: String) -> String {
let processed = input.to_uppercase();
processed // 返回处理后的结果
}
fn main() {
let user_input = String::from("hello");
let result = process_input(user_input);
// println!("{}", user_input); // ❌ user_input 已经无效了
println!("{}", result); // ✅ 正确
}
如果你想保留原始输入怎么办?
fn process_input(input: &String) -> String {
input.to_uppercase() // 借用,不转移所有权
}
fn main() {
let user_input = String::from("hello");
let result = process_input(&user_input); // 传递引用
println!("{}", user_input); // ✅ 还能用
println!("{}", result); // ✅ 正确
}
fn main() {
let s1 = String::from("hello");
let s2 = &s1; // s2 借用 s1
let s3 = &s1; // s3 也借用 s1
println!("{}", s1); // ✅ 所有者还能用
println!("{}", s2); // ✅ 借用者也能用
println!("{}", s3); // ✅ 多个借用者
}
这就是借用的威力! 多个引用可以同时存在,只要它们都是不可变借用。

核心要点:
&T 不转移所有权金句:
Rust 的所有权系统就像个操心的老妈:"这个变量你拿好了吗?别人用了你还想用?不行!用完记得扔啊,别堆着!"
下一篇咱们讲讲变量与基本类型:
let、const、static 有啥区别?敬请期待!
💬 互动问题:
你学 Rust 时被所有权折磨过吗?在评论区分享你的踩坑经历吧!😄