Rust智能指针
概述
智能指针是结构体,通过实现Deref和Drop trait,模拟指针行为并管理内存生命周期
- Deref: 允许智能指针像普通指针一样被解引用
- Drop: 定义指针离开作用域时的清理逻辑
Rc
Rc
struct RcBox<T> {
data: T, // 实际数据
strong: usize, // 强引用计数
weak: usize, // 弱引用计数
}
特性
- 共享所有权
- 引用计数
- 不可变借用: Rc仅提供原数据的不可变引用
- 单线程限制:非线程安全
方法
// Rc::new(Value :T) -> Rc<T>
use std::rc::Rc;
let rc = Rc::new(42); // 引用计数 =1
// Rc::clone(&rc) -> Rc<T>,克隆Rc示例,引用计数加一
let rc_clone = Rc::clone(&rc); // 引用计数 = 2
// Rc::strong_count(&rc) -> usize, 获取强引用计数
println!("Count: {}", Rc::strong_count(&rc)); // 输出 2
// Rc::downgrade(&rc) -> Weak<T> 创建弱引用,不增加引用计数
// Rc 可能导致循环引用,造成内存泄漏。此时需结合 Weak<T> 弱引用打破循环
let weak = Rc::downgrade(&rc)
Box
Box
- 存储动态大小类型:如递归、trait
- 避免大对象栈溢出:栈空间有限,大对象更适合在堆上分配
- 明确所有权转移:强制数据在堆上唯一归属,简化所有权规则
特性
- 唯一所有权
- 自动内存释放:当Box离开作用域时,会调用Drop trait自动释放堆内存
- 零运行时开销:Box本身仅是一个指针
- 支持Deref和Drop:解引用和自动清理
方法
let b = Box::new(43); // 43在堆上存储
println!("value = {}", *b);
let b2 = b; // 所有权转移到b2
典型场景
- 递归类型如链表,rust要求类型在编译期有固定大小,递归类型无法直接定义,使用Box分配在堆上
enum Node{
Cons(i32, Box<Node>), // 使用 Box 包裹下一节点
Nil
}
let Node = Node::Cons(1, Box::new(Node::Cons(2, Box::new(Node::Nil))));
- 转移大对象所有权:直接传递大对象时如数组,会引发拷贝效率低
fn process(data: Box<[u8; 1_000_000]>) { /* ... */ }
let big_data = Box::new([0u8; 1_000_000]); // 堆上分配
process(big_data); // 仅传递指针,无数据拷贝
- Trait对象动态分发,Box
trait Animal {
fn speak(&self);
}
struct Dog;
impl Animal for Dog { /* ... */ }
struct Cat;
impl Animal for Cat { /* ... */ }
let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog),
Box::new(Cat),
];
Arc
Arc
struct ArcInner<T> {
data: T,
strong: AtomicUsize, // 原子强引用计数
weak: AtomicUsize, // 原子弱引用计数
}
特性:
- 线程安全
- 共享所有权
- 不可变借用
- 性能开销:比普通操作略慢
方法
use std::sync::Arc;
let data = Arc::new(42);
let data_clone = Arc::clone(&data);
println!("Count: {}", Arc::strong_count(&data));
let treahe_data = Arc::clone(&data);
let handle = std::thread::spawn(move||{println!("thread value: {}", *thread_data);});
handle.join().unwrap();
共享可变数据
结合Mutex
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter_clone = Arc::clone(&counter);
handles.push(std::thread::spawn(move || {
let mut num = counter_clone.lock().unwrap();
*num += 1;
});
}
RefCell
RefCell是Rust中用于实现内部可变性的智能指针,允许在拥有不可变引用(&T)时修改数据,突破Rust编译期借用检查的限制,将借用规则的验证从编译推迟到运行时
struct RefCell<T> {
data: UnsafeCell<T>, // 实际存储数据(允许内部可变性)
borrows: Cell<BorrowFlag>, // 追踪当前借用状态
}
// BorrowFlag 的简化表示:
// - 正数:活跃的不可变借用数量
// - -1:存在活跃的可变借用
type BorrowFlag = isize;
- borrow():检查 borrows,若为 -1 则 panic!,否则不可变计数 +1。
- borrow_mut():检查 borrows,若不为 0 则 panic!,否则标记为 -1。
- Drop 实现:释放 Ref 或 RefMut 时更新计数器。
特性
- 内部可变性:允许通过不可变引用修改数据
- 运行时借用检查: 在运行时追踪活跃的借用,违反则触发panic!
- 单线程专用
方法
- borrow() -> Ref
:获取不可变引用,运行时检查是否存在活跃的可变引用 - borrow_mut() -> RefMut
: 获取可变引用,运行时检查是否存在任何活跃的借用 - try_borrow()和try_borrow_ut(): 非阻塞尝试获取引用,返回Result而非触发panic!
借用规则(运行时验证)
- 不可变借用 Ref
: 可同时存在多个 - 可变借用RefMut
: 同一时间只能存在一个,且不可与不可变借用共存
// 一个日志系统:对外不可变,但内部可记录日志
struct Logger {
logs: RefCell<Vec<String>>,
}
impl Logger {
fn new() -> Self {
Logger { logs: RefCell::new(Vec::new()) }
}
// 即使 &self 是不可变引用,也能修改内部数据
fn log(&self, message: &str) {
self.logs.borrow_mut().push(message.to_string());
}
}
let logger = Logger::new();
logger.log("Event 1"); // 通过不可变引用修改数据
use std::rc::Rc;
use std::cell::RefCell;
// 多个所有者共享可变数据
let shared_data = Rc::new(RefCell::new(42));
let clone1 = Rc::clone(&shared_data);
let clone2 = Rc::clone(&shared_data);
*clone1.borrow_mut() += 1; // 修改数据
*clone2.borrow_mut() *= 2; // 再次修改
println!("Result: {}", shared_data.borrow()); // 输出 86
Cow
Copy on Write,写时克隆, Cow
pub enum Cow<'a, B>
where
B: 'a + ToOwned + ?Sized,
{
Borrowed(&'a B), // 借用数据
Owned(<B as ToOwned>::Owned), // 拥有克隆后的数据
}
特性
- 两种状态:Borrowed: 持有不可变引用,无所有权;Owned:持有数据的克隆副本,拥有所有权
- 零拷贝优化: 默认不克隆数据,修改时克隆
- 实现Deref
方法
// Cow::to_mut(): 获取可变引用
// 如果当前为Borrowed,触发克隆并转为Owned
// 如果为Owned,直接返回可变引用
let mut cow = Cow::Borrowed("hell");
let s = cow.to_mut(); // &mut String
s.push_str("word!);
// Cow::into_owned():提取所有权数据
// 如果为Owned,直接返回数据
// 如果为Borrowed,触发克隆并返回数据
let cow: Cow<str> = Cow::Borrowed("hello");
let owned = cow.into_owned(); // 克隆 生成String
use std::borrow::Cow;
fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> {
for i in 0..input.len() {
let v = input[i];
if v < 0 {
// Clones into a vector if not already owned.
input.to_mut()[i] = -v;
}
}
input
}
fn owned_mutation() -> Result<(), &'static str> {
// Of course this is also the case if a mutation does occur. In this
// case the call to `to_mut()` returns a reference to the same data as
// before.
let slice = vec![-1, 0, 1];
let mut input = Cow::from(slice);
match abs_all(&mut input) {
Cow::Owned(_) => Ok(()),
_ => Err("Expected owned value"),
}
}
use std::borrow::Cow;
fn process_input(input: &str) -> Cow<str> {
if input.len() > 10 { // 假设需要修改长字符串
Cow::Owned(input.to_uppercase()) // 触发克隆
} else {
Cow::Borrowed(input) // 保持借用
}
}
let short = process_input("hello"); // Cow::Borrowed("hello")
let long = process_input("this is a long string"); // Cow::Owned("THIS IS A LONG STRING")

浙公网安备 33010602011771号