跳到主要内容

Rc 智能指针

Rc<T>(Reference Counted)是一个引用计数智能指针,允许多个所有者共享同一份数据。它只能在单线程环境中使用。

什么是 Rc?

Rc<T> 是一个智能指针,它:

  • 允许多个所有者共享数据
  • 使用引用计数跟踪所有者数量
  • 只能在单线程中使用
  • 数据是不可变的(除非配合 RefCell)

基本用法

创建和克隆 Rc

use std::rc::Rc;

fn main() {
// 创建 Rc
let data = Rc::new(String::from("Hello, Rc!"));
println!("引用计数: {}", Rc::strong_count(&data));

// 克隆 Rc(增加引用计数)
let data2 = Rc::clone(&data);
let data3 = data.clone(); // 等价写法

println!("引用计数: {}", Rc::strong_count(&data));
println!("data: {}", data);
println!("data2: {}", data2);
println!("data3: {}", data3);

// 检查是否指向同一数据
println!("data 和 data2 相等: {}", Rc::ptr_eq(&data, &data2));
}

引用计数管理

use std::rc::Rc;

fn main() {
let data = Rc::new(vec![1, 2, 3, 4, 5]);
println!("初始引用计数: {}", Rc::strong_count(&data));

{
let data2 = Rc::clone(&data);
println!("克隆后引用计数: {}", Rc::strong_count(&data));

{
let data3 = Rc::clone(&data);
println!("再次克隆后引用计数: {}", Rc::strong_count(&data));
} // data3 离开作用域

println!("data3 离开后引用计数: {}", Rc::strong_count(&data));
} // data2 离开作用域

println!("最终引用计数: {}", Rc::strong_count(&data));
} // data 离开作用域,数据被释放

使用场景

1. 共享数据结构

use std::rc::Rc;

#[derive(Debug)]
struct Node {
value: i32,
children: Vec<Rc<Node>>,
}

impl Node {
fn new(value: i32) -> Rc<Self> {
Rc::new(Node {
value,
children: Vec::new(),
})
}
}

fn main() {
let leaf1 = Node::new(1);
let leaf2 = Node::new(2);

let branch = Rc::new(Node {
value: 10,
children: vec![Rc::clone(&leaf1), Rc::clone(&leaf2)],
});

let root = Rc::new(Node {
value: 100,
children: vec![Rc::clone(&branch), Rc::clone(&leaf1)],
});

println!("树结构: {:#?}", root);
println!("leaf1 引用计数: {}", Rc::strong_count(&leaf1)); // 2
println!("branch 引用计数: {}", Rc::strong_count(&branch)); // 1
}

2. 图数据结构

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct GraphNode {
value: i32,
neighbors: RefCell<Vec<Rc<GraphNode>>>,
}

impl GraphNode {
fn new(value: i32) -> Rc<Self> {
Rc::new(GraphNode {
value,
neighbors: RefCell::new(Vec::new()),
})
}

fn add_neighbor(&self, neighbor: Rc<GraphNode>) {
self.neighbors.borrow_mut().push(neighbor);
}

fn get_neighbors(&self) -> Vec<Rc<GraphNode>> {
self.neighbors.borrow().clone()
}
}

fn main() {
let node1 = GraphNode::new(1);
let node2 = GraphNode::new(2);
let node3 = GraphNode::new(3);

// 创建图的连接
node1.add_neighbor(Rc::clone(&node2));
node1.add_neighbor(Rc::clone(&node3));
node2.add_neighbor(Rc::clone(&node3));
node3.add_neighbor(Rc::clone(&node1));

println!("节点1的邻居数量: {}", node1.get_neighbors().len());
println!("节点1引用计数: {}", Rc::strong_count(&node1)); // 2 (node3 引用了它)
}

3. 缓存系统

use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;

#[derive(Debug, Clone)]
struct CacheEntry {
key: String,
value: String,
access_count: RefCell<usize>,
}

impl CacheEntry {
fn new(key: String, value: String) -> Self {
CacheEntry {
key,
value,
access_count: RefCell::new(0),
}
}

fn access(&self) -> &str {
*self.access_count.borrow_mut() += 1;
&self.value
}

fn get_access_count(&self) -> usize {
*self.access_count.borrow()
}
}

struct Cache {
entries: HashMap<String, Rc<CacheEntry>>,
}

impl Cache {
fn new() -> Self {
Cache {
entries: HashMap::new(),
}
}

fn insert(&mut self, key: String, value: String) -> Rc<CacheEntry> {
let entry = Rc::new(CacheEntry::new(key.clone(), value));
self.entries.insert(key, Rc::clone(&entry));
entry
}

fn get(&self, key: &str) -> Option<Rc<CacheEntry>> {
self.entries.get(key).cloned()
}

fn stats(&self) {
for (key, entry) in &self.entries {
println!("键: {}, 访问次数: {}, 引用计数: {}",
key, entry.get_access_count(), Rc::strong_count(entry));
}
}
}

fn main() {
let mut cache = Cache::new();

// 插入缓存项
let entry1 = cache.insert("user:123".to_string(), "Alice".to_string());
let entry2 = cache.insert("user:456".to_string(), "Bob".to_string());

// 获取并使用缓存项
if let Some(user) = cache.get("user:123") {
println!("用户: {}", user.access());
println!("用户: {}", user.access());
}

// 创建额外的引用
let user_ref = cache.get("user:123").unwrap();
println!("用户引用: {}", user_ref.access());

cache.stats();
}

Rc 与 RefCell 结合

内部可变性

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Counter {
value: RefCell<i32>,
}

impl Counter {
fn new(initial: i32) -> Rc<Self> {
Rc::new(Counter {
value: RefCell::new(initial),
})
}

fn increment(&self) {
*self.value.borrow_mut() += 1;
}

fn get(&self) -> i32 {
*self.value.borrow()
}
}

fn main() {
let counter = Counter::new(0);
let counter2 = Rc::clone(&counter);
let counter3 = Rc::clone(&counter);

counter.increment();
counter2.increment();
counter3.increment();

println!("计数器值: {}", counter.get()); // 3
println!("引用计数: {}", Rc::strong_count(&counter)); // 3
}

共享可变状态

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct SharedList {
items: RefCell<Vec<String>>,
}

impl SharedList {
fn new() -> Rc<Self> {
Rc::new(SharedList {
items: RefCell::new(Vec::new()),
})
}

fn add(&self, item: String) {
self.items.borrow_mut().push(item);
}

fn get_all(&self) -> Vec<String> {
self.items.borrow().clone()
}

fn len(&self) -> usize {
self.items.borrow().len()
}
}

fn process_list(list: Rc<SharedList>, prefix: &str) {
for i in 1..=3 {
list.add(format!("{}-{}", prefix, i));
}
}

fn main() {
let shared_list = SharedList::new();

// 多个函数共享同一个列表
process_list(Rc::clone(&shared_list), "A");
process_list(Rc::clone(&shared_list), "B");

println!("列表内容: {:?}", shared_list.get_all());
println!("列表长度: {}", shared_list.len());
println!("引用计数: {}", Rc::strong_count(&shared_list));
}

循环引用问题

问题演示

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Option<Rc<Node>>>,
children: RefCell<Vec<Rc<Node>>>,
}

impl Node {
fn new(value: i32) -> Rc<Self> {
Rc::new(Node {
value,
parent: RefCell::new(None),
children: RefCell::new(Vec::new()),
})
}

fn add_child(&self, child: Rc<Node>) {
child.parent.borrow_mut().replace(Rc::new(Node {
value: self.value,
parent: RefCell::new(None),
children: RefCell::new(Vec::new()),
}));
self.children.borrow_mut().push(child);
}
}

fn demonstrate_cycle_problem() {
let parent = Node::new(1);
let child = Node::new(2);

// 这会创建循环引用!
// parent.add_child(Rc::clone(&child));

println!("演示循环引用问题");
// 内存泄漏:parent 引用 child,child 引用 parent
}

使用 Weak 解决循环引用

use std::rc::{Rc, Weak};
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Option<Weak<Node>>>, // 使用 Weak 避免循环引用
children: RefCell<Vec<Rc<Node>>>,
}

impl Node {
fn new(value: i32) -> Rc<Self> {
Rc::new(Node {
value,
parent: RefCell::new(None),
children: RefCell::new(Vec::new()),
})
}

fn add_child(parent: &Rc<Node>, child: Rc<Node>) {
child.parent.borrow_mut().replace(Rc::downgrade(parent));
parent.children.borrow_mut().push(child);
}

fn get_parent(&self) -> Option<Rc<Node>> {
self.parent.borrow().as_ref()?.upgrade()
}
}

fn main() {
let parent = Node::new(1);
let child1 = Node::new(2);
let child2 = Node::new(3);

Node::add_child(&parent, Rc::clone(&child1));
Node::add_child(&parent, Rc::clone(&child2));

println!("父节点值: {}", parent.value);
println!("子节点数量: {}", parent.children.borrow().len());

if let Some(parent_ref) = child1.get_parent() {
println!("child1 的父节点值: {}", parent_ref.value);
}

println!("parent 强引用计数: {}", Rc::strong_count(&parent));
println!("parent 弱引用计数: {}", Rc::weak_count(&parent));
}

性能考虑

引用计数开销

use std::rc::Rc;
use std::time::Instant;

fn main() {
let n = 1_000_000;

// 测试 Rc 克隆性能
let data = Rc::new(vec![1, 2, 3, 4, 5]);
let start = Instant::now();

let mut refs = Vec::new();
for _ in 0..n {
refs.push(Rc::clone(&data));
}

println!("Rc 克隆时间: {:?}", start.elapsed());
println!("最终引用计数: {}", Rc::strong_count(&data));

// 清理
let start = Instant::now();
refs.clear();
println!("清理时间: {:?}", start.elapsed());
}

内存使用比较

use std::rc::Rc;
use std::mem;

fn main() {
let data = vec![1, 2, 3, 4, 5];
let rc_data = Rc::new(data.clone());

println!("Vec 大小: {} bytes", mem::size_of_val(&data));
println!("Rc 大小: {} bytes", mem::size_of_val(&rc_data));
println!("Rc 指针大小: {} bytes", mem::size_of::<Rc<Vec<i32>>>());

// Rc 的额外开销
println!("引用计数开销: 每个 Rc 实例 {} bytes",
mem::size_of::<Rc<Vec<i32>>>());
}

实际应用示例

观察者模式

use std::rc::Rc;
use std::cell::RefCell;

trait Observer {
fn update(&self, message: &str);
}

struct ConcreteObserver {
name: String,
}

impl ConcreteObserver {
fn new(name: String) -> Self {
ConcreteObserver { name }
}
}

impl Observer for ConcreteObserver {
fn update(&self, message: &str) {
println!("{} 收到消息: {}", self.name, message);
}
}

struct Subject {
observers: RefCell<Vec<Rc<dyn Observer>>>,
}

impl Subject {
fn new() -> Self {
Subject {
observers: RefCell::new(Vec::new()),
}
}

fn attach(&self, observer: Rc<dyn Observer>) {
self.observers.borrow_mut().push(observer);
}

fn notify(&self, message: &str) {
for observer in self.observers.borrow().iter() {
observer.update(message);
}
}
}

fn main() {
let subject = Subject::new();

let observer1: Rc<dyn Observer> = Rc::new(ConcreteObserver::new("观察者1".to_string()));
let observer2: Rc<dyn Observer> = Rc::new(ConcreteObserver::new("观察者2".to_string()));

subject.attach(Rc::clone(&observer1));
subject.attach(Rc::clone(&observer2));

subject.notify("重要通知!");

println!("observer1 引用计数: {}", Rc::strong_count(&observer1));
}

最佳实践

1. 何时使用 Rc

// 适合使用 Rc 的场景:
// - 需要多个所有者共享数据
// - 单线程环境
// - 数据结构如树、图
// - 缓存系统

// 不适合的场景:
// - 多线程环境(使用 Arc)
// - 需要可变性(配合 RefCell)
// - 简单的单一所有权(使用 Box)

2. 避免循环引用

use std::rc::{Rc, Weak};

// 好的做法:使用 Weak 打破循环
struct Parent {
children: Vec<Rc<Child>>,
}

struct Child {
parent: Weak<Parent>, // 使用 Weak 引用
}

3. 性能优化

use std::rc::Rc;

fn optimize_rc_usage() {
// 好:重用 Rc
let data = Rc::new(expensive_computation());
let data1 = Rc::clone(&data);
let data2 = Rc::clone(&data);

// 避免:重复计算
// let data1 = Rc::new(expensive_computation());
// let data2 = Rc::new(expensive_computation());
}

fn expensive_computation() -> Vec<i32> {
(0..1000).collect()
}

Rc 是单线程环境下共享所有权的理想选择,但需要注意循环引用问题。下一节我们将学习多线程环境下的 Arc 智能指针。