Skip to content

智能指针

智能指针是一种数据结构,不仅像指针一样指向数据,还拥有额外的元数据和功能。Rust 中最常用的智能指针包括 Box<T>Rc<T>Arc<T>RefCell<T>

Box<T> 是最简单的智能指针,将数据存储在堆上。

fn main() {
let b = Box::new(5);
println!("b = {}", b);
} // b 离开作用域,堆上的数据被释放
// 编译错误!无法确定大小
// 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))))));
}
fn main() {
let large_data = Box::new([0u8; 1000000]); // 1MB 数据在堆上
process(large_data); // 只传递指针,而不是复制整个数组
}
fn process(data: Box<[u8; 1000000]>) {
println!("处理 {} 字节的数据", data.len());
}
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 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())
}

Rust 会自动进行解引用强制转换:

fn hello(name: &str) {
println!("Hello, {}", name);
}
fn main() {
let m = Box::new(String::from("Rust"));
hello(&m); // Box<String> -> &String -> &str
}

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<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<T> 允许在运行时而非编译时检查借用规则。

use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
// 获取可变借用
*data.borrow_mut() += 1;
// 获取不可变借用
println!("值: {}", data.borrow());
}
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! 已经有不可变借用
}

结合 RcRefCell 实现多所有者可变数据:

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<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<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!("没有父节点"),
}
}
类型引用计数阻止释放使用场景
Rc<T> / Arc<T>增加拥有数据
Weak<T>不增加临时引用,避免循环
场景推荐类型
堆分配、递归类型Box<T>
单线程多所有者Rc<T>
多线程多所有者Arc<T>
内部可变性(单线程)RefCell<T>
内部可变性(多线程)Mutex<T> / RwLock<T>
避免循环引用Weak<T>

使用 Box 创建一个链表:

enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
fn main() {
// 创建: 1 -> 2 -> 3 -> Nil
let list = // 你的代码
// 打印链表
}

使用 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);
}

使用 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
}

使用 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 的并发编程,了解如何安全地在多线程环境下共享数据。