智能指针
智能指针是一种数据结构,不仅像指针一样指向数据,还拥有额外的元数据和功能。Rust 中最常用的智能指针包括 Box<T>、Rc<T>、Arc<T> 和 RefCell<T>。
Box:堆分配
Section titled “Box:堆分配”Box<T> 是最简单的智能指针,将数据存储在堆上。
fn main() { let b = Box::new(5); println!("b = {}", b);} // b 离开作用域,堆上的数据被释放1. 递归类型
Section titled “1. 递归类型”// 编译错误!无法确定大小// enum List {// Cons(i32, List),// Nil,// }
// 使用 Box 解决enum List { Cons(i32, Box<List>), Nil,}
use List::{Cons, Nil};
fn main() { let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));}2. 大数据避免复制
Section titled “2. 大数据避免复制”fn main() { let large_data = Box::new([0u8; 1000000]); // 1MB 数据在堆上 process(large_data); // 只传递指针,而不是复制整个数组}
fn process(data: Box<[u8; 1000000]>) { println!("处理 {} 字节的数据", data.len());}3. Trait 对象
Section titled “3. Trait 对象”trait Draw { fn draw(&self);}
struct Button;impl Draw for Button { fn draw(&self) { println!("绘制按钮"); }}
fn main() { let drawable: Box<dyn Draw> = Box::new(Button); drawable.draw();}Deref 和 Drop Trait
Section titled “Deref 和 Drop Trait”Deref:解引用
Section titled “Deref:解引用”Deref trait 允许自定义解引用运算符 * 的行为:
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) }}
impl<T> Deref for MyBox<T> { type Target = T;
fn deref(&self) -> &Self::Target { &self.0 }}
fn main() { let x = 5; let y = MyBox::new(x);
assert_eq!(5, *y); // 等价于 *(y.deref())}解引用强制转换
Section titled “解引用强制转换”Rust 会自动进行解引用强制转换:
fn hello(name: &str) { println!("Hello, {}", name);}
fn main() { let m = Box::new(String::from("Rust")); hello(&m); // Box<String> -> &String -> &str}Drop:资源清理
Section titled “Drop:资源清理”Drop trait 在值离开作用域时执行清理:
struct CustomSmartPointer { data: String,}
impl Drop for CustomSmartPointer { fn drop(&mut self) { println!("释放 CustomSmartPointer,数据: {}", self.data); }}
fn main() { let c = CustomSmartPointer { data: String::from("hello"), }; let d = CustomSmartPointer { data: String::from("world"), }; println!("创建了 CustomSmartPointer");}// 输出顺序:// 创建了 CustomSmartPointer// 释放 CustomSmartPointer,数据: world// 释放 CustomSmartPointer,数据: hello使用 std::mem::drop 提前释放:
fn main() { let c = CustomSmartPointer { data: String::from("hello"), }; println!("创建了 CustomSmartPointer"); drop(c); // 提前释放 println!("CustomSmartPointer 已被释放");}Rc:引用计数
Section titled “Rc:引用计数”Rc<T>(Reference Counting)允许多个所有者共享数据。
use std::rc::Rc;
fn main() { let a = Rc::new(String::from("hello")); println!("引用计数: {}", Rc::strong_count(&a)); // 1
let b = Rc::clone(&a); // 增加引用计数 println!("引用计数: {}", Rc::strong_count(&a)); // 2
{ let c = Rc::clone(&a); println!("引用计数: {}", Rc::strong_count(&a)); // 3 }
println!("引用计数: {}", Rc::strong_count(&a)); // 2}use std::rc::Rc;
enum List { Cons(i32, Rc<List>), Nil,}
use List::{Cons, Nil};
fn main() { let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); let b = Cons(3, Rc::clone(&a)); // b 和 c 共享 a let c = Cons(4, Rc::clone(&a));}注意:Rc<T> 只能用于单线程场景,且数据是不可变的。
RefCell:内部可变性
Section titled “RefCell:内部可变性”RefCell<T> 允许在运行时而非编译时检查借用规则。
use std::cell::RefCell;
fn main() { let data = RefCell::new(5);
// 获取可变借用 *data.borrow_mut() += 1;
// 获取不可变借用 println!("值: {}", data.borrow());}运行时借用检查
Section titled “运行时借用检查”use std::cell::RefCell;
fn main() { let data = RefCell::new(5);
let r1 = data.borrow(); let r2 = data.borrow(); // OK,多个不可变借用
// let r3 = data.borrow_mut(); // panic! 已经有不可变借用}Rc<RefCell> 组合
Section titled “Rc<RefCell> 组合”结合 Rc 和 RefCell 实现多所有者可变数据:
use std::cell::RefCell;use std::rc::Rc;
#[derive(Debug)]enum List { Cons(Rc<RefCell<i32>>, Rc<List>), Nil,}
use List::{Cons, Nil};
fn main() { let value = Rc::new(RefCell::new(5));
let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a)); let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));
// 修改共享的值 *value.borrow_mut() += 10;
println!("a = {:?}", a); println!("b = {:?}", b); println!("c = {:?}", c);}Arc:原子引用计数
Section titled “Arc:原子引用计数”Arc<T>(Atomic Reference Counting)是线程安全的 Rc<T>。
use std::sync::Arc;use std::thread;
fn main() { let data = Arc::new(vec![1, 2, 3]);
let handles: Vec<_> = (0..3) .map(|i| { let data = Arc::clone(&data); thread::spawn(move || { println!("线程 {} 看到: {:?}", i, data); }) }) .collect();
for handle in handles { handle.join().unwrap(); }}Weak:弱引用
Section titled “Weak:弱引用”Weak<T> 不增加引用计数,避免循环引用:
use std::cell::RefCell;use std::rc::{Rc, Weak};
#[derive(Debug)]struct Node { value: i32, parent: RefCell<Weak<Node>>, // 弱引用父节点 children: RefCell<Vec<Rc<Node>>>, // 强引用子节点}
fn main() { let leaf = Rc::new(Node { value: 3, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![]), });
let branch = Rc::new(Node { value: 5, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![Rc::clone(&leaf)]), });
// 设置 leaf 的父节点 *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
// 访问父节点(可能为空) match leaf.parent.borrow().upgrade() { Some(parent) => println!("leaf 的父节点值: {}", parent.value), None => println!("没有父节点"), }}强引用 vs 弱引用
Section titled “强引用 vs 弱引用”| 类型 | 引用计数 | 阻止释放 | 使用场景 |
|---|---|---|---|
Rc<T> / Arc<T> | 增加 | 是 | 拥有数据 |
Weak<T> | 不增加 | 否 | 临时引用,避免循环 |
| 场景 | 推荐类型 |
|---|---|
| 堆分配、递归类型 | Box<T> |
| 单线程多所有者 | Rc<T> |
| 多线程多所有者 | Arc<T> |
| 内部可变性(单线程) | RefCell<T> |
| 内部可变性(多线程) | Mutex<T> / RwLock<T> |
| 避免循环引用 | Weak<T> |
练习 1:递归类型
Section titled “练习 1:递归类型”使用 Box 创建一个链表:
enum List { Cons(i32, Box<List>), Nil,}
use List::{Cons, Nil};
fn main() { // 创建: 1 -> 2 -> 3 -> Nil let list = // 你的代码
// 打印链表}练习 2:Rc 共享数据
Section titled “练习 2:Rc 共享数据”使用 Rc 让多个变量共享同一数据:
use std::rc::Rc;
fn main() { let data = Rc::new(String::from("共享数据"));
let a = // 克隆 Rc let b = // 克隆 Rc
println!("引用计数: {}", Rc::strong_count(&data)); println!("a: {}", a); println!("b: {}", b);}练习 3:RefCell 内部可变性
Section titled “练习 3:RefCell 内部可变性”使用 RefCell 在不可变结构中修改数据:
use std::cell::RefCell;
struct Counter { value: RefCell<i32>,}
impl Counter { fn new() -> Self { Counter { value: RefCell::new(0), } }
fn increment(&self) { // 修改 value }
fn get(&self) -> i32 { // 返回 value }}
fn main() { let counter = Counter::new(); counter.increment(); counter.increment(); println!("计数: {}", counter.get()); // 应输出: 2}练习 4:避免循环引用
Section titled “练习 4:避免循环引用”使用 Weak 创建双向链接的节点结构:
use std::cell::RefCell;use std::rc::{Rc, Weak};
struct Node { value: i32, next: RefCell<Option<Rc<Node>>>, prev: RefCell<Option<Weak<Node>>>, // 使用弱引用}
fn main() { let node1 = Rc::new(Node { value: 1, next: RefCell::new(None), prev: RefCell::new(None), });
let node2 = Rc::new(Node { value: 2, next: RefCell::new(None), prev: RefCell::new(None), });
// 设置双向链接 // node1.next -> node2 // node2.prev -> node1 (弱引用)}了解了智能指针后,下一章我们将学习 Rust 的并发编程,了解如何安全地在多线程环境下共享数据。