Logo

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 本身不可变,需配合其他类型(如 RefCellMutex)实现内部可变性。


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::Rcstd::sync::Arc

8. 总结

选择依据: • 单线程共享不可变数据 → Rc<T>。 • 多线程共享数据 → Arc<T>(常配合 MutexRwLock 修改数据)。 • 设计原则:优先考虑所有权和借用规则,仅在必要时使用引用计数,避免过度依赖导致复杂度上升。

© 2025 All rights reservedBuilt with Flowershow Cloud

Built with LogoFlowershow Cloud