Ac和Rc实现1vN所有权机制
Rust 中 Rc 与 Arc 知识点总结
1. 核心概念
• 所有权问题:Rust 默认一个值只有一个所有者,但某些场景(如图结构、多线程)需要多所有权机制。
• 引用计数:通过记录数据的引用次数管理所有权,计数归零时自动释放数据。
• 智能指针类型:
• Rc<T>
:单线程下的引用计数智能指针,不可变借用。
• Arc<T>
:原子化引用计数的多线程安全版本(Atomic Rc
),支持跨线程共享数据。
2. 关键机制
• 引用计数操作:
• Rc::clone(&a)
:浅拷贝,仅增加引用计数(非数据深拷贝)。
• Rc::strong_count(&a)
:获取当前强引用计数值。
• drop(a)
:显式释放引用,计数减 1。
• 不可变性:Rc/Arc
本身不可变,需配合其他类型(如 RefCell
、Mutex
)实现内部可变性。
3. 使用场景
• 单线程共享数据(Rc<T>
):
use std::rc::Rc;
let a = Rc::new(String::from("hello"));
let b = Rc::clone(&a); // 引用计数 +1
• 多线程共享数据(Arc<T>
):
use std::sync::Arc;
let s = Arc::new(String::from("thread-safe"));
let handle = thread::spawn(move || println!("{}", s)); // 跨线程传递
4. 线程安全与性能
• Rc
的局限性:非原子操作引用计数,无法跨线程(未实现 Send
trait)。
• Arc
的原子性:通过原子操作保证线程安全,但性能略低于 Rc
。
• 错误示例:
// ❌ 错误:Rc 不能跨线程
let s = Rc::new("data");
thread::spawn(move || { println!("{}", s); });
5. 示例解析
• 解决多所有权问题:
use std::rc::Rc;
struct Owner { name: String }
struct Gadget { id: i32, owner: Rc<Owner> }
let owner = Rc::new(Owner { name: "Alice".into() });
let gadget1 = Gadget { id: 1, owner: Rc::clone(&owner) };
let gadget2 = Gadget { id: 2, owner: Rc::clone(&owner) };
// owner 数据被两个 Gadget 共享,计数为 3(包括初始创建)
• 跨线程共享:
use std::sync::Arc;
let s = Arc::new("shared");
for _ in 0..10 {
let s = Arc::clone(&s);
thread::spawn(move || println!("{}", s));
}
6. 配合可变性
• 内部可变性:需结合 RefCell
(单线程)或 Mutex
(多线程):
// 单线程:Rc + RefCell
use std::rc::Rc;
use std::cell::RefCell;
let data = Rc::new(RefCell::new(42));
*data.borrow_mut() += 1;
// 多线程:Arc + Mutex
use std::sync::{Arc, Mutex};
let data = Arc::new(Mutex::new(42));
let handle = thread::spawn(move || {
*data.lock().unwrap() += 1;
});
7. 核心区别
特性 | Rc<T> | Arc<T> |
---|---|---|
线程安全 | ❌ 单线程 | ✅ 多线程 |
引用计数操作 | 非原子操作 | 原子操作 |
性能 | 更高 | 略低(原子操作开销) |
模块路径 | std::rc::Rc | std::sync::Arc |
8. 总结
• 选择依据:
• 单线程共享不可变数据 → Rc<T>
。
• 多线程共享数据 → Arc<T>
(常配合 Mutex
或 RwLock
修改数据)。
• 设计原则:优先考虑所有权和借用规则,仅在必要时使用引用计数,避免过度依赖导致复杂度上升。