跳到主要内容

Weak 智能指针

Weak<T> 是一个不拥有数据所有权的智能指针,它提供了对 Rc<T>Arc<T> 管理数据的弱引用,主要用于解决循环引用问题。

什么是 Weak?

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

  • 不拥有数据所有权
  • 不影响引用计数
  • 可能指向已释放的数据
  • 用于打破循环引用

基本用法

创建和使用 Weak

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

fn main() {
let strong_ref = Rc::new(String::from("Hello, Weak!"));
println!("强引用计数: {}", Rc::strong_count(&strong_ref));

// 创建弱引用
let weak_ref: Weak<String> = Rc::downgrade(&strong_ref);
println!("强引用计数: {}", Rc::strong_count(&strong_ref));
println!("弱引用计数: {}", Rc::weak_count(&strong_ref));

// 尝试升级弱引用
match weak_ref.upgrade() {
Some(strong) => println!("升级成功: {}", strong),
None => println!("升级失败,数据已被释放"),
}

// 释放强引用
drop(strong_ref);

// 再次尝试升级
match weak_ref.upgrade() {
Some(strong) => println!("升级成功: {}", strong),
None => println!("升级失败,数据已被释放"),
}
}

Weak 的生命周期

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

fn main() {
let weak_ref: Weak<String>;

{
let strong_ref = Rc::new(String::from("临时数据"));
weak_ref = Rc::downgrade(&strong_ref);

println!("作用域内 - 强引用计数: {}", Rc::strong_count(&strong_ref));
println!("作用域内 - 弱引用计数: {}", Rc::weak_count(&strong_ref));

// strong_ref 在作用域结束时被释放
}

// 数据已被释放,弱引用无法升级
match weak_ref.upgrade() {
Some(data) => println!("数据仍然存在: {}", data),
None => println!("数据已被释放"),
}
}

解决循环引用

父子关系示例

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

#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Option<Weak<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(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 print_tree(&self, depth: usize) {
let indent = " ".repeat(depth);
println!("{}节点值: {}", indent, self.value);

for child in self.children.borrow().iter() {
child.print_tree(depth + 1);
}
}
}

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

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

println!("树结构:");
root.print_tree(0);

// 验证父子关系
if let Some(parent) = grandchild.get_parent() {
println!("孙子节点 {} 的父节点是 {}", grandchild.value, parent.value);
}

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

双向链表

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

#[derive(Debug)]
struct ListNode {
value: i32,
next: RefCell<Option<Rc<ListNode>>>,
prev: RefCell<Option<Weak<ListNode>>>,
}

impl ListNode {
fn new(value: i32) -> Rc<Self> {
Rc::new(ListNode {
value,
next: RefCell::new(None),
prev: RefCell::new(None),
})
}
}

struct DoublyLinkedList {
head: Option<Rc<ListNode>>,
tail: Option<Weak<ListNode>>,
}

impl DoublyLinkedList {
fn new() -> Self {
DoublyLinkedList {
head: None,
tail: None,
}
}

fn push_back(&mut self, value: i32) {
let new_node = ListNode::new(value);

match self.tail.as_ref().and_then(|weak| weak.upgrade()) {
Some(old_tail) => {
old_tail.next.borrow_mut().replace(Rc::clone(&new_node));
new_node.prev.borrow_mut().replace(Rc::downgrade(&old_tail));
}
None => {
self.head = Some(Rc::clone(&new_node));
}
}

self.tail = Some(Rc::downgrade(&new_node));
}

fn print_forward(&self) {
let mut current = self.head.as_ref().map(Rc::clone);
print!("向前遍历: ");

while let Some(node) = current {
print!("{} ", node.value);
current = node.next.borrow().as_ref().map(Rc::clone);
}
println!();
}

fn print_backward(&self) {
if let Some(tail_weak) = &self.tail {
if let Some(tail) = tail_weak.upgrade() {
let mut current = Some(tail);
print!("向后遍历: ");

while let Some(node) = current {
print!("{} ", node.value);
current = node.prev.borrow().as_ref().and_then(|weak| weak.upgrade());
}
println!();
}
}
}
}

fn main() {
let mut list = DoublyLinkedList::new();

list.push_back(1);
list.push_back(2);
list.push_back(3);
list.push_back(4);

list.print_forward();
list.print_backward();
}

缓存系统应用

带有弱引用的缓存

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

#[derive(Debug)]
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
}
}

struct WeakCache {
entries: RefCell<HashMap<String, Weak<CacheEntry>>>,
}

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

fn get_or_create<F>(&self, key: &str, factory: F) -> Rc<CacheEntry>
where
F: FnOnce() -> String,
{
let mut entries = self.entries.borrow_mut();

// 尝试从缓存获取
if let Some(weak_entry) = entries.get(key) {
if let Some(entry) = weak_entry.upgrade() {
return entry;
} else {
// 弱引用已失效,移除
entries.remove(key);
}
}

// 创建新条目
let new_entry = Rc::new(CacheEntry::new(key.to_string(), factory()));
entries.insert(key.to_string(), Rc::downgrade(&new_entry));
new_entry
}

fn cleanup(&self) {
let mut entries = self.entries.borrow_mut();
entries.retain(|_, weak| weak.upgrade().is_some());
}

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

fn active_entries(&self) -> usize {
self.entries.borrow().values()
.filter(|weak| weak.upgrade().is_some())
.count()
}
}

fn main() {
let cache = WeakCache::new();

// 创建一些缓存条目
let entry1 = cache.get_or_create("key1", || "value1".to_string());
let entry2 = cache.get_or_create("key2", || "value2".to_string());

println!("缓存大小: {}", cache.size());
println!("活跃条目: {}", cache.active_entries());

// 使用条目
println!("访问 entry1: {}", entry1.access());
println!("访问 entry2: {}", entry2.access());

// 获取相同的条目(从缓存)
let entry1_again = cache.get_or_create("key1", || "new_value1".to_string());
println!("再次访问 entry1: {}", entry1_again.access());

// 释放强引用
drop(entry2);

println!("释放 entry2 后:");
println!("缓存大小: {}", cache.size());
println!("活跃条目: {}", cache.active_entries());

// 清理失效的弱引用
cache.cleanup();
println!("清理后缓存大小: {}", cache.size());

// 尝试获取已释放的条目(会重新创建)
let entry2_new = cache.get_or_create("key2", || "new_value2".to_string());
println!("重新创建 entry2: {}", entry2_new.access());
}

观察者模式优化

使用 Weak 避免内存泄漏

use std::rc::{Rc, Weak};
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<Weak<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(Rc::downgrade(observer));
}

fn notify(&self, message: &str) {
let mut observers = self.observers.borrow_mut();

// 清理失效的观察者并通知活跃的观察者
observers.retain(|weak_observer| {
if let Some(observer) = weak_observer.upgrade() {
observer.update(message);
true // 保留这个观察者
} else {
false // 移除失效的观察者
}
});
}

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

fn active_observer_count(&self) -> usize {
self.observers.borrow().iter()
.filter(|weak| weak.upgrade().is_some())
.count()
}
}

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()));
let observer3: Rc<dyn Observer> = Rc::new(ConcreteObserver::new("观察者3".to_string()));

subject.attach(&observer1);
subject.attach(&observer2);
subject.attach(&observer3);

println!("初始观察者数量: {}", subject.observer_count());
subject.notify("第一条消息");

// 释放一个观察者
drop(observer2);

println!("\n释放观察者2后:");
println!("观察者数量: {}", subject.observer_count());
println!("活跃观察者数量: {}", subject.active_observer_count());

subject.notify("第二条消息");

println!("\n通知后观察者数量: {}", subject.observer_count());
}

多线程环境中的 Weak

Arc 和 Weak 结合

use std::sync::{Arc, Weak};
use std::thread;
use std::time::Duration;

struct Resource {
id: u32,
data: String,
}

impl Resource {
fn new(id: u32, data: String) -> Self {
Resource { id, data }
}
}

impl Drop for Resource {
fn drop(&mut self) {
println!("资源 {} 被释放", self.id);
}
}

fn main() {
let resource = Arc::new(Resource::new(1, "重要数据".to_string()));
let weak_ref = Arc::downgrade(&resource);

let weak_clone = weak_ref.clone();
let handle = thread::spawn(move || {
for i in 0..5 {
thread::sleep(Duration::from_millis(100));

match weak_clone.upgrade() {
Some(res) => {
println!("线程 - 第{}次访问资源: {} - {}", i + 1, res.id, res.data);
}
None => {
println!("线程 - 第{}次访问失败,资源已被释放", i + 1);
break;
}
}
}
});

// 主线程等待一段时间后释放资源
thread::sleep(Duration::from_millis(250));
println!("主线程释放资源");
drop(resource);

handle.join().unwrap();

// 验证弱引用无法升级
match weak_ref.upgrade() {
Some(_) => println!("意外:资源仍然存在"),
None => println!("确认:资源已被完全释放"),
}
}

性能考虑

Weak 的开销

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

fn main() {
let n = 1_000_000;

// 测试 Rc 克隆
let strong_ref = Rc::new(42);
let start = Instant::now();
let mut strong_refs = Vec::new();
for _ in 0..n {
strong_refs.push(Rc::clone(&strong_ref));
}
println!("Rc 克隆时间: {:?}", start.elapsed());

// 测试 Weak 创建
let start = Instant::now();
let mut weak_refs = Vec::new();
for _ in 0..n {
weak_refs.push(Rc::downgrade(&strong_ref));
}
println!("Weak 创建时间: {:?}", start.elapsed());

// 测试 Weak 升级
let start = Instant::now();
let mut upgrade_count = 0;
for weak_ref in &weak_refs {
if weak_ref.upgrade().is_some() {
upgrade_count += 1;
}
}
println!("Weak 升级时间: {:?}", start.elapsed());
println!("成功升级次数: {}", upgrade_count);

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

最佳实践

1. 何时使用 Weak

// 适合使用 Weak 的场景:
// - 打破循环引用
// - 父子关系(子持有父的弱引用)
// - 缓存系统
// - 观察者模式
// - 回调系统

// 不适合的场景:
// - 需要保证数据存在
// - 频繁的升级操作
// - 简单的数据共享

2. 安全使用 Weak

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

fn safe_weak_usage(weak_ref: &Weak<String>) -> Option<usize> {
// 好的做法:检查升级结果
weak_ref.upgrade().map(|strong| strong.len())

// 避免:假设升级总是成功
// weak_ref.upgrade().unwrap().len() // 可能 panic
}

3. 及时清理失效的 Weak 引用

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

struct WeakContainer {
items: RefCell<Vec<Weak<String>>>,
}

impl WeakContainer {
fn cleanup(&self) {
self.items.borrow_mut().retain(|weak| weak.upgrade().is_some());
}

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

Weak 智能指针是解决循环引用问题的重要工具,合理使用可以避免内存泄漏并实现灵活的数据结构设计。