Rust 智能指针
【摘要】 Rust 提供了多种智能指针(Smart Pointers),用于在堆上分配内存、管理资源(如文件、网络连接)或实现特殊行为(如引用计数)。以下是 Rust 中主要智能指针的用法和区别: 1. 核心智能指针 (1) Box<T>:堆分配用途:将数据存储在堆上,明确表示所有权转移。特点:适用于大型数据(避免栈溢出)。实现 Deref 和 Drop trait。支持递归类型(如链表、树)。 示例...
Rust 提供了多种智能指针(Smart Pointers),用于在堆上分配内存、管理资源(如文件、网络连接)或实现特殊行为(如引用计数)。以下是 Rust 中主要智能指针的用法和区别:
1. 核心智能指针
(1) Box<T>:堆分配
- 用途:将数据存储在堆上,明确表示所有权转移。
- 特点:
- 适用于大型数据(避免栈溢出)。
- 实现
Deref和Droptrait。 - 支持递归类型(如链表、树)。
示例
fn main() {
// 基本用法:堆分配
let x = Box::new(5);
println!("x = {}", x); // 自动解引用(Deref)
// 递归类型(如链表)
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
println!("{:?}", list);
}
(2) Rc<T>:单线程引用计数
- 用途:在单线程中共享数据的多个所有权。
- 特点:
- 通过
Rc::clone()增加引用计数。 - 当计数归零时自动释放内存。
- 非线程安全(不实现
Send和Sync)。
- 通过
示例
use std::rc::Rc;
fn main() {
let a = Rc::new(10);
let b = Rc::clone(&a); // 引用计数 +1
let c = Rc::clone(&a);
println!("a: {}, b: {}, c: {}", a, b, c);
println!("引用计数: {}", Rc::strong_count(&a)); // 输出 3
}
(3) Arc<T>:多线程引用计数
- 用途:在多线程中安全共享数据(线程安全版
Rc<T>)。 - 特点:
- 使用原子操作管理引用计数(性能略低于
Rc)。 - 通常与
Mutex/RwLock配合实现可变共享数据。
- 使用原子操作管理引用计数(性能略低于
示例
use std::sync::Arc;
use std::thread;
fn main() {
let counter = Arc::new(0);
let mut handles = vec![];
for _ in 0..5 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
*counter.lock().unwrap() += 1; // 需要 Mutex 保护可变性
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
(4) Cell<T> 和 RefCell<T>:内部可变性
- 用途:在不可变引用下修改数据(违反 Rust 常规借用规则)。
- 区别:
Cell<T>:适用于Copy类型(如u32),通过set/get方法操作。RefCell<T>:适用于任意类型,通过运行时借用检查(borrow/borrow_mut)。
示例
use std::cell::{Cell, RefCell};
fn main() {
// Cell<T> 示例
let x = Cell::new(10);
x.set(20); // 不可变引用下修改值
println!("x = {}", x.get());
// RefCell<T> 示例
let y = RefCell::new(vec![1, 2, 3]);
{
let mut v = y.borrow_mut(); // 获取可变借用
v.push(4);
} // 借用结束,自动释放
println!("y = {:?}", y.borrow());
}
(5) Mutex<T> 和 RwLock<T>:线程同步
- 用途:保护共享数据的可变性。
- 区别:
Mutex<T>:独占访问(一次仅一个线程可访问)。RwLock<T>:允许多个读或单个写(读多写少场景更高效)。
示例
use std::sync::{Mutex, RwLock};
fn main() {
// Mutex 示例
let m = Mutex::new(42);
{
let mut num = m.lock().unwrap();
*num = 100;
}
// RwLock 示例
let rw = RwLock::new(vec![]);
{
let mut v = rw.write().unwrap();
v.push(1);
}
{
let v = rw.read().unwrap();
println!("v = {:?}", *v);
}
}
2. 智能指针的选择指南
| 场景 | 推荐智能指针 |
|---|---|
| 单线程堆分配 | Box<T> |
| 单线程共享所有权 | Rc<T> |
| 多线程共享所有权 | Arc<T> |
| 内部可变性(单线程) | Cell<T> / RefCell<T> |
| 线程安全可变共享数据 | Arc<Mutex<T>> / Arc<RwLock<T>> |
| 避免循环引用 | Arc<T> + Weak<T> |
3. 高级用法
(1) 自定义智能指针
通过实现 Deref 和 Drop trait 创建自定义智能指针:
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> Self {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> Drop for MyBox<T> {
fn drop(&mut self) {
println!("Dropping MyBox!");
}
}
fn main() {
let x = MyBox::new(5);
println!("x = {}", *x); // 自动调用 deref
}
(2) 避免循环引用
使用 Weak<T> 打破 Arc<T> 的循环引用:
use std::rc::{Arc, Weak};
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>,
children: RefCell<Vec<Arc<Node>>>,
}
fn main() {
let leaf = Arc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
let root = Arc::new(Node {
value: 1,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Arc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Arc::downgrade(&root); // Weak 引用
}
总结
Box<T>:最简单的堆分配,适合明确所有权转移。Rc<T>/Arc<T>:共享所有权,单线程用Rc,多线程用Arc。Cell<T>/RefCell<T>:内部可变性,绕过 Rust 借用规则。Mutex<T>/RwLock<T>:线程安全可变共享数据。Weak<T>:避免循环引用,常与Arc配合使用。
根据场景选择合适的智能指针,可以高效管理内存和资源,同时保持 Rust 的安全性。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)