题目
理解Rust中的所有权和借用
信息
- 类型:问答
- 难度:⭐⭐
考点
所有权系统,借用规则,可变引用
快速回答
Rust的所有权系统通过三条核心规则保证内存安全:
- 每个值有且只有一个所有者
- 值在所有者离开作用域时自动释放
- 可通过引用(&T)借用值,但同一作用域内:
- 允许存在多个不可变引用
- 或仅一个可变引用(&mut T)
- 不可同时存在可变和不可变引用
原理说明
Rust的所有权系统在编译期强制执行内存安全规则,无需垃圾回收。核心机制包括:
- 所有权转移:赋值或传参时默认移动(move)语义,原变量失效
- 借用(Borrowing):通过引用临时访问数据,不获取所有权
- 生命周期:编译器验证引用的有效性,防止悬垂指针
代码示例
// 所有权转移示例
let s1 = String::from("hello");
let s2 = s1; // s1的所有权转移到s2
// println!("{}", s1); // 错误!s1已失效
// 合法借用示例
let mut s = String::from("rust");
{
let r1 = &s; // 不可变借用
let r2 = &s; // 允许多个不可变借用
println!("{}, {}", r1, r2);
} // r1,r2离开作用域
let r3 = &mut s; // 可变借用
r3.push_str("!"); // 修改数据
// let r4 = &s; // 错误!已有可变借用时禁止不可变借用
最佳实践
- 优先使用不可变引用(&T)保证并发安全
- 缩小可变引用的作用域:
let mut data = vec![1,2,3]; // 正确做法:用花括号限制作用域 { let ref_mut = &mut data; ref_mut.push(4); } let ref_imm = &data; // 可变引用离开作用域后可创建不可变引用 - 对Copy类型(如i32)使用克隆而非引用
常见错误
- 错误1:违反借用规则
let mut s = String::new(); let r1 = &mut s; let r2 = &mut s; // 编译错误:不能多次可变借用 - 错误2:返回局部变量引用
fn dangling() -> &String { let s = String::from("error"); &s // 错误!s离开作用域被释放 } - 错误3:迭代器失效
let mut vec = vec![1,2,3]; for item in &vec { vec.push(*item); // 错误!循环中同时持有不可变和可变借用 }
扩展知识
- 内部可变性:通过Cell/RefCell在不可变引用下修改数据
- 生命周期标注:解决跨作用域引用的有效性,如
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str - Arc<Mutex<T>>:线程间共享可变数据的标准方案
- NLL(Non-Lexical Lifetimes):Rust 2018起引用的作用域精确到最后一次使用而非作用域结束