我来总结 Rust 中所有权、引用、借用、可变、不可变的注意事项:
Rust 核心概念总结
1. 所有权(Ownership)
核心规则
- 每个值都有一个所有者
- 同一时间只能有一个所有者
- 所有者离开作用域时,值被丢弃
注意事项
// ❌ 错误:所有权转移后不能使用原变量
let s1 = String::from("hello");
let s2 = s1; // 所有权转移
// println!("{}", s1); // 错误!s1 已失效
// ✅ 正确:使用克隆
let s1 = String::from("hello");
let s2 = s1.clone(); // 克隆,不转移所有权
println!("{}", s1); // ✅ s1 仍然有效
关键点
-
移动语义:赋值会转移所有权
-
Copy 类型:实现了 Copy trait 的类型赋值会复制而不是移动
-
函数参数:传递参数会转移所有权
-
返回值:函数返回值会转移所有权给调用者
2. 引用(References)
核心概念
注意事项
// ❌ 错误:悬垂引用
fn dangle() -> &String {
let s = String::from("hello");
&s // 错误!s 离开作用域后被释放
}
// ✅ 正确:返回所有权
fn no_dangle() -> String {
let s = String::from("hello");
s // 返回所有权
}
关键点
-
语法:
&T 表示引用
-
解引用:
* 操作符访问引用指向的值
-
生命周期:引用必须有效,不能指向已释放的内存
3. 借用(Borrowing)
核心规则
- 同一时间只能有一个可变引用,或多个不可变引用
- 引用必须始终有效
- 可变和不可变引用不能同时存在
注意事项
// ❌ 错误:多个可变引用
let mut x = 5;
let r1 = &mut x;
let r2 = &mut x; // 错误!
// ✅ 正确:作用域隔离
let mut x = 5;
{
let r1 = &mut x;
} // r1 离开作用域
let r2 = &mut x; // ✅ 可以
关键点
-
借用检查器:编译时检查借用规则
-
非词法作用域(NLL):引用可以在作用域结束前失效
-
最后一次使用:引用最后一次使用后可以失效
4. 可变(Mutable)
核心概念
-
可变变量:使用
mut 关键字
-
可变引用:使用
&mut 语法
-
允许修改:可以修改值或引用指向的值
注意事项
// ❌ 错误:修改不可变变量
let x = 5;
x = 10; // 错误!
// ✅ 正确:使用 mut
let mut x = 5;
x = 10; // ✅ 可以修改
关键点
-
默认不可变:Rust 默认变量不可变
-
显式可变:需要明确声明
mut
-
可变引用:
&mut T 允许修改引用指向的值
5. 不可变(Immutable)
核心概念
-
默认不可变:Rust 默认变量不可变
-
不可变引用:
&T 不允许修改
-
安全性:防止意外修改
注意事项
// ❌ 错误:修改不可变引用
let x = 5;
let r = &x;
*r = 10; // 错误!
// ✅ 正确:使用可变引用
let mut x = 5;
let r = &mut x;
*r = 10; // ✅ 可以修改
关键点
-
默认安全:不可变性提供安全保障
-
显式修改:需要明确声明可变性
-
多线程安全:不可变性有助于并发安全
6. 综合注意事项
规则对比表
| 概念 |
语法 |
所有权 |
可修改性 |
注意事项 |
| 所有权 |
let x = value |
✅ 获取 |
❌ 不可变 |
转移后原变量失效 |
| 不可变引用 |
&T |
❌ 借用 |
❌ 不可修改 |
多个可存在 |
| 可变引用 |
&mut T |
❌ 借用 |
✅ 可修改 |
只能存在一个 |
常见错误
1. 所有权错误
// ❌ 使用已转移所有权的变量
let s1 = String::from("hello");
let s2 = s1;
println!("{}", s1); // 错误!
2. 借用冲突
// ❌ 可变和不可变引用同时存在
let mut x = 5;
let r1 = &x;
let r2 = &mut x; // 错误!
3. 悬垂引用
// ❌ 引用指向已释放的内存
fn bad() -> &String {
let s = String::from("hello");
&s // 错误!
}
最佳实践
1. 明确所有权转移
let s1 = String::from("hello");
let s2 = s1; // 明确知道 s1 已失效
// 使用 s2 而不是 s1
2. 合理使用引用
// 使用引用避免所有权转移
fn calculate_length(s: &String) -> usize {
s.len()
}
3. 作用域隔离
// 使用作用域隔离借用
let mut x = 5;
{
let r = &mut x;
*r = 10;
}
// 可以再次使用 x
4. 生命周期标注
// 为复杂情况添加生命周期标注
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
7. 总结要点
-
所有权是基础:理解所有权是掌握 Rust 的关键
-
借用是工具:引用和借用是灵活使用值的工具
-
可变性是选择:默认不可变,需要修改时显式声明
-
编译器是朋友:借用检查器帮助发现错误
-
安全性优先:Rust 的设计优先考虑内存安全