这是Rust最核心、最独特、也是最让初学者困惑的特性——所有权系统。理解所有权是掌握Rust的关键,它让Rust无需垃圾回收器就能保证内存安全。
学习目标:理解所有权三大规则、掌握移动与克隆、学会借用与引用、理解生命周期基础
fn main() {
{
let s = String::from("hello"); // s从此时开始有效
println!("{}", s); // 使用s
} // 作用域结束,s被drop,内存释放
// println!("{}", s); // ❌ 错误!s已不存在
// 对比:栈上的简单类型
let x = 5; // i32在栈上
let y = x; // 复制值(Copy语义)
println!("x={}, y={}", x, y); // ✅ 都有效
}
✅ 验证通过
对于堆上分配的数据(如String),赋值操作会移动所有权,而不是复制数据:
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1的所有权移动到s2
// println!("{}", s1); // ❌ 编译错误!s1已失效
println!("{}", s2); // ✅ s2有效
// 函数传参也会移动所有权
let s3 = String::from("world");
takes_ownership(s3);
// println!("{}", s3); // ❌ s3已被移动到函数中
// 函数返回值也转移所有权
let s4 = gives_ownership();
println!("s4 = {}", s4);
// 获取所有权再返回
let s5 = String::from("rust");
let s6 = takes_and_gives_back(s5);
// println!("{}", s5); // ❌ 已移动
println!("s6 = {}", s6);
}
fn takes_ownership(s: String) {
println!("获取所有权: {}", s);
} // s被drop
fn gives_ownership() -> String {
String::from("来自函数的值")
}
fn takes_and_gives_back(s: String) -> String {
s // 原样返回
}
✅ 验证通过
如果确实需要深拷贝,使用clone():
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝!
println!("s1 = {}, s2 = {}", s1, s2); // ✅ 两个都有效
// clone代价较高——堆上数据被完整复制
let v1 = vec![1, 2, 3, 4, 5];
let v2 = v1.clone();
println!("v1 = {:?}, v2 = {:?}", v1, v2);
}
✅ 验证通过
实现了Copy特征的类型在赋值时会自动复制,不会移动:
fn main() {
// 这些类型实现了Copy:
let x: i32 = 42;
let y = x; // 复制
println!("x={}, y={}", x, y); // ✅ 都有效
let b: bool = true;
let b2 = b; // 复制
println!("b={}, b2={}", b, b2); // ✅ 都有效
let c: char = '🦀';
let c2 = c; // 复制
println!("c={}, c2={}", c, c2); // ✅ 都有效
let t: (i32, f64) = (1, 2.0);
let t2 = t; // 复制(元素都是Copy的)
println!("t={:?}, t2={:?}", t, t2); // ✅ 都有效
let arr: [i32; 3] = [1, 2, 3];
let arr2 = arr; // 复制(固定大小且元素是Copy的)
println!("arr={:?}, arr2={:?}", arr, arr2); // ✅ 都有效
// ⚠️ 不可Copy的类型:
// let s = String::from("hello");
// let s2 = s; // 移动!String不是Copy
// println!("{}", s); // ❌
}
✅ 验证通过
每次都移动所有权太麻烦了!Rust提供了引用机制——借用值而不获取所有权。
fn calculate_length(s: &String) -> usize { // 借用String
s.len()
} // s离开作用域,但因为它不拥有所有权,不会drop
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // &创建引用
// s1仍然有效!
println!("'{}'的长度是{}", s1, len);
// 同时存在多个不可变引用 ✅
let r1 = &s1;
let r2 = &s1;
println!("r1={}, r2={}", r1, r2);
}
✅ 验证通过
fn append_suffix(s: &mut String) {
s.push_str(", Rust!");
}
fn main() {
let mut s = String::from("Hello");
append_suffix(&mut s);
println!("{}", s); // "Hello, Rust!"
// ⚠️ 同一时刻只能有一个可变引用
let r1 = &mut s;
// let r2 = &mut s; // ❌ 编译错误!不能同时有两个&mut
r1.push_str(" 🦀");
println!("{}", r1);
// r1离开作用域后可以创建新的可变引用
let r2 = &mut s;
r2.push_str("!!");
println!("{}", r2);
}
✅ 验证通过
规则1:同一时刻,你可以拥有任意数量的不可变引用(&T),或者恰好一个可变引用(&mut T)。
规则2:引用必须始终有效(不能有悬垂引用)。
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 不可变引用
let r2 = &s; // 不可变引用
println!("r1={}, r2={}", r1, r2);
// r1和r2在此之后不再使用(NLL)
let r3 = &mut s; // ✅ 允许!因为r1/r2已不再使用
r3.push_str(" world");
println!("r3={}", r3);
}
✅ 验证通过
// ❌ 编译错误!Rust阻止了悬垂引用
// fn dangle() -> &String {
// let s = String::from("hello");
// &s // s在函数结束时被drop,返回的引用指向已释放的内存
// }
// ✅ 正确做法:返回所有权
fn no_dangle() -> String {
let s = String::from("hello");
s // 移动所有权给调用者
}
fn main() {
let s = no_dangle();
println!("{}", s);
}
✅ 验证通过
struct Buffer {
data: Vec<u8>,
}
impl Buffer {
fn new(capacity: usize) -> Self {
Buffer {
data: Vec::with_capacity(capacity),
}
}
fn write(&mut self, bytes: &[u8]) { // 借用slice,不需要所有权
self.data.extend_from_slice(bytes);
}
fn read(&self) -> &[u8] { // 返回不可变引用
&self.data
}
fn len(&self) -> usize {
self.data.len()
}
fn clear(&mut self) {
self.data.clear();
}
fn into_vec(self) -> Vec<u8> { // 消耗self,返回所有权
self.data
}
}
fn main() {
let mut buf = Buffer::new(100);
buf.write(b"Hello");
buf.write(b" ");
buf.write(b"Rust!");
println!("缓冲区内容: {}", String::from_utf8_lossy(buf.read()));
println!("长度: {}", buf.len());
// 读取不影响所有权
let data = buf.read();
println!("数据: {:?}", data);
println!("长度: {}", buf.len()); // buf仍然可用
// 消耗buffer获取内部数据
let vec = buf.into_vec();
// buf.len(); // ❌ buf已被消耗
println!("提取的Vec: {:?}", vec);
}
✅ 验证通过
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}", s1); // 修复这行
}
编写函数reverse(s: &str) -> String,接受字符串引用,返回反转后的新字符串。不修改原始字符串。
编写函数count_words(text: &str) -> usize,统计字符串中的单词数。
编写结构体Stats,包含字段count和total。实现方法add(&mut self, value: f64)和average(&self) -> f64。
🔒 下一课解锁:字符串与切片 —— 深入理解Rust的字符串类型
use std::thread;
use std::sync::{Arc, Mutex};
// 所有权系统如何保证并发安全
fn main() {
// ❌ 编译器阻止不安全的并发
// let mut data = vec![1, 2, 3];
// thread::spawn(|| { data.push(4); }); // ❌ data可能被多个线程访问
// ✅ 使用Arc+Mutex安全共享
let data = Arc::new(Mutex::new(vec![1, 2, 3]));
let mut handles = vec![];
for i in 0..3 {
let data = Arc::clone(&data);
handles.push(thread::spawn(move || {
let mut d = data.lock().unwrap();
d.push(i + 10);
}));
}
for h in handles { h.join().unwrap(); }
println!("并发结果: {:?}", data.lock().unwrap());
// ✅ 使用通道通信
use std::sync::mpsc;
let (tx, rx) = mpsc::channel();
for i in 0..5 {
let tx = tx.clone();
thread::spawn(move || { tx.send(i).unwrap(); });
}
drop(tx); // 关闭原始发送端
let received: Vec<_> = rx.iter().collect();
println!("通道接收: {:?}", received);
}
✅ 验证通过
| 场景 | 方案 | 示例 |
|---|---|---|
| 函数只读数据 | &T | fn print(s: &str) |
| 函数修改数据 | &mut T | fn push(v: &mut Vec) |
| 函数消费数据 | T | fn process(s: String) |
| 返回新数据 | T | fn create() -> String |
| 返回借用数据 | &T + 生命周期 | fn get(&self) -> &str |
| 多所有者 | Rc<T>/Arc<T> | Rc::clone(&data) |
| 内部可变 | RefCell<T> | cell.borrow_mut() |
| 避免引用 | Owned + Clone | 使用String而非&str |