🦀 Rust 所有权机制
📚 目录
- 什么是值类型和引用类型?值语义和引用语义?
- 什么是所有权?为什么 Rust 需要它?
- 所有权的三大原则(修正版)
- 移动语义 vs 复制语义:变量赋值到底发生了什么?
- 绑定和作用域:值的“活多久”与“谁管它”
- 引用和借用:本质是什么?为什么这么设计?
- 借用的三条规则(逐条解释+示例)
- 生命周期:引用能用多久?
- 生命周期参数:为什么需要?怎么用?
- Rust 的借用检查器是怎么帮你“兜底”的?
- 总结与学习建议
1️⃣ 什么是值类型和引用类型?值语义和引用语义?
✔️ 值类型(Value Type)
变量里存的是实际的数据。
let x = 10; // x 是 i32 值类型
✔️ 引用类型(Reference Type)
变量里存的是指向其他数据的“地址”。
let x = 10;
let y = &x; // y 是对 x 的引用,类型是 &i32
🔍 值语义 vs 引用语义
概念 | 解释 | 示例 |
---|---|---|
值语义 | 每次赋值都复制/移动数据本身 | let b = a; |
引用语义 | 多个变量共享同一个值的访问权(指针) | let r = &a; |
2️⃣ 什么是所有权?为什么 Rust 需要它?
在 Rust 中,所有权机制用于管理内存,确保内存安全,不靠垃圾回收(GC),也不手动 free
。
通俗理解:谁拥有这个数据,谁负责清理它。
- 避免悬空引用
- 自动释放资源(变量离开作用域)
- 避免多线程数据竞争
3️⃣ Rust 所有权的三大原则
- 每个值在任意时刻只能有一个所有者。
- 当所有者离开作用域,值会被自动释放。
- 一个值只能有一个可变引用,或任意多个不可变引用,但不能同时存在。
let a = String::from("hello");
let b = a; // a 的所有权移动到 b
// println!("{}", a); // ❌ 报错:a 被移动了
4️⃣ 移动语义 vs 复制语义
let a = 10; // a 是 Copy 类型
let b = a; // 自动复制,a 仍然有效let s1 = String::from("Hi");
let s2 = s1; // 所有权移动,s1 失效
想要深拷贝,用 .clone()
:
let s1 = String::from("Rust");
let s2 = s1.clone(); // ✅ s1 仍有效
5️⃣ 绑定和作用域
变量只在其“作用域”内有效:
{let name = String::from("Tom");println!("{}", name);
} // name 离开作用域,自动释放
作用域由 {}
包围,函数、if、match、循环等都会创建作用域。
6️⃣ 引用和借用:到底有什么区别?
✅ 本质解释:
- 引用(reference)是“变量的地址”(通过
&
获取) - 借用(borrowing)是“使用别人的值但不拥有它”的行为
🧠 所以:“引用”是工具,而“借用”是动作。
举例说明:
let s = String::from("Rust");
let r = &s; // 你用 &s 创建了一个“引用”,这就是“借用了 s 的值”
概念 | 通俗解释 | Rust 表现形式 |
---|---|---|
引用 | 地址 | &a 、&mut a |
借用 | 临时借东西不拥有 | let r = &a |
7️⃣ 借用的三条规则(逐条解释+示例)
规则 1:借用不能比原值活得更久(生命周期限制)
fn dangle() -> &String {let s = String::from("oops");&s // ❌ s 被释放,引用悬空
}
规则 2:可变借用具有独占性
let mut s = String::from("hi");
let r1 = &mut s;
// let r2 = &mut s; // ❌ 同时只能有一个可变借用
规则 3:有不可变借用时,不能再借出可变引用
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
// let r3 = &mut s; // ❌ 报错:不能混合借用
📌 原则:只读可以多个,可写只能一个,不能混用。
8️⃣ 生命周期:引用能用多久?
生命周期就是引用在内存中能活多久。
Rust 不允许引用指向已释放的内存:
fn dangle() -> &String {let s = String::from("hi");&s // ❌ 返回了已经释放的 s
}
9️⃣ 生命周期参数:为什么需要?怎么用?
当函数的参数和返回值中有引用,Rust 要你说明它们“谁活得久”。这就是“生命周期参数”。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {if x.len() > y.len() { x } else { y }
}
解释:
<'a>
是生命周期参数x
、y
、返回值 都必须活得跟'a
一样久
📌 一般来说,编译器可以自动推断。但在函数返回引用时,必须显式写出生命周期参数。
🔟 Rust 的借用检查器是怎么帮你的?
Rust 有一个非常强大的“借用检查器”(Borrow Checker),会在编译阶段检查:
- 有没有悬空引用?
- 可变引用有没有被别名?
- 是否混用了可变和不可变引用?
- 生命周期是否符合规则?
🧠 你写错了引用规则,Rust 不会让你运行,而是直接编译失败。
✅ 借用检查器就是 Rust 安全的“守门员”!它会严格替你把关内存安全。
🔚 总结与学习建议
概念 | 通俗解释 |
---|---|
所有权 | 谁拥有值,谁负责清理 |
移动 | 所有权转移,原值失效 |
复制 | 创建副本,两个变量互不影响 |
借用 | 临时访问但不拥有,分可变/不可变 |
引用 | 表示地址,借用的语法工具 |
生命周期 | 引用能活多久(不能活得比原值久) |
生命周期参数 | 显式告诉编译器“谁和谁一样久” |
!