欢迎光临
我们一直在努力

【Rust入门知识点学与练】第29课:智能指针进阶与内部可变性

知识点:Box 回顾 — 堆上分配

// Box<T> 把数据放在堆上,栈上只存指针
// 适用场景:
// 1. 编译时大小未知的类型(如递归类型)
// 2. 大量数据需要转移所有权但不想拷贝
// 3. 只关心值是否实现了某个 trait(trait 对象)

// 递归类型必须用 Box
#[derive(Debug)]
enum List {
Cons(i32, Box<List>), // Box 让编译器知道大小
Nil,
}

fn build_list() -> List {
List::Cons(1, Box::new(
List::Cons(2, Box::new(
List::Cons(3, Box::new(
List::Nil
))
))
))
}

fn print_list(list: &List) {
match list {
List::Cons(val, next) => {
print!("{} ", val);
print_list(next);
}
List::Nil => println!("Nil"),
}
}

// Box 实现 Deref,可以像普通引用一样使用
fn deref_example() {
let b = Box::new(5);
println!("值: {}", *b); // 自动解引用
println!("值: {}", b); // Display 也会自动解引用
}

// Box 实现 Drop,离开作用域自动释放堆内存
fn drop_example() {
{
let b = Box::new(String::from("hello"));
println!("{}", b);
} // b 离开作用域,堆上的 String 被释放
}

fn main() {
let list = build_list();
print_list(&list); // 1 2 3 Nil

deref_example();
drop_example();
}

知识点:Rc — 引用计数(单线程共享所有权)

use std::rc::Rc;

// Rc<T> 允许多个所有者共享同一份数据
// 引用计数:每多一个 Rc 指向数据,计数+1
// 当计数归零时,数据被释放
// ⚠️ 只能用于单线程!多线程用 Arc<T>

#[derive(Debug)]
struct SharedData {
value: String,
}

fn rc_basics() {
let data = Rc::new(SharedData {
value: String::from("共享数据"),
});

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

let clone1 = Rc::clone(&data); // 计数+1(不是深拷贝!)
println!("引用计数: {}", Rc::strong_count(&data)); // 2

let clone2 = Rc::clone(&data); // 计数+1
println!("引用计数: {}", Rc::strong_count(&data)); // 3

// 多个 Rc 指向同一份数据
println!("data: {}", data.value);
println!("clone1: {}", clone1.value);
println!("clone2: {}", clone2.value);

// 验证是同一份数据(比较指针地址)
println!("同一数据? {}", Rc::ptr_eq(&data, &clone1)); // true

drop(clone2);
println!("drop clone2 后计数: {}", Rc::strong_count(&data)); // 2

drop(clone1);
println!("drop clone1 后计数: {}", Rc::strong_count(&data)); // 1
}

// 实际场景:多个结构体共享同一份数据
#[derive(Debug)]
struct Node {
value: i32,
parent: Rc<Node>, // 共享父节点
}

fn shared_parent() {
let root = Rc::new(Node { value: 0, parent: Rc::new(Node { value: 1, parent: Rc::new(Node { value: 1, parent: unsafe { Rc::from_raw(std::ptr::null()) } }) }) });
// 上面的写法太复杂,实际中用 RefCell 或 Option

// 更好的方式
let root = Rc::new(Node2 { value: 0, children: vec![] });
// … 见后面 RefCell 的例子
}

// 简单示例:共享配置
#[derive(Debug)]
struct Config {
db_host: String,
max_connections: u32,
}

#[derive(Debug)]
struct Service {
name: String,
config: Rc<Config>, // 共享配置
}

fn shared_config() {
let config = Rc::new(Config {
db_host: String::from("localhost"),
max_connections: 100,
});

let services = vec![
Service { name: String::from("用户服务"), config: Rc::clone(&config) },
Service { name: String::from("订单服务"), config: Rc::clone(&config) },
Service { name: String::from("支付服务"), config: Rc::clone(&config) },
];

for svc in &services {
println!("{}: db={}, max_conn={}", svc.name, svc.config.db_host, svc.config.max_connections);
}

println!("配置引用计数: {}", Rc::strong_count(&config)); // 4(3个service + 1个原始)
}

fn main() {
rc_basics();
println!("—");
shared_config();
}

知识点:RefCell — 内部可变性

use std::cell::RefCell;

// RefCell<T> 允许在不可变引用下修改数据
// 把借用检查从编译期推迟到运行期
// 违反借用规则时会 panic(而非编译错误)

fn refcell_basics() {
let data = RefCell::new(vec![1, 2, 3]);

// borrow():不可变借用(可以多个)
println!("数据: {:?}", data.borrow());

// borrow_mut():可变借用(只能一个,且不能和 borrow 共存)
data.borrow_mut().push(4);
println!("修改后: {:?}", data.borrow());

// 运行期检查:违反规则会 panic
// let r1 = data.borrow();
// let r2 = data.borrow_mut(); // ❌ panic! 同时有不可变和可变借用
}

// 运行期借用检查 vs 编译期借用检查
fn comparison() {
// 编译期检查(普通引用)
let mut v = vec![1, 2, 3];
let r1 = &v;
// let r2 = &mut v; // ❌ 编译错误!
println!("{}", r1[0]);

// 运行期检查(RefCell)
let cell = RefCell::new(vec![1, 2, 3]);
let r1 = cell.borrow();
// let r2 = cell.borrow_mut(); // ❌ 运行时 panic!
println!("{}", r1[0]);
// 区别:RefCell 的代码能编译通过,但在运行时 panic
}

// RefCell 的实际用途:在只能获取不可变引用的地方修改数据
// 例如:回调函数、缓存、计数器

#[derive(Debug)]
struct Cache {
data: RefCell<std::collections::HashMap<String, String>>,
}

impl Cache {
fn new() -> Self {
Cache {
data: RefCell::new(std::collections::HashMap::new()),
}
}

// &self 而不是 &mut self!
// 因为内部用 RefCell 实现了可变性
fn get_or_insert(&self, key: &str, value: &str) -> String {
let mut cache = self.data.borrow_mut();
if let Some(v) = cache.get(key) {
v.clone()
} else {
cache.insert(key.to_string(), value.to_string());
value.to_string()
}
}

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

fn cache_example() {
let cache = Cache::new(); // 注意:不是 mut!

// 通过不可变引用调用"修改"方法
let val1 = cache.get_or_insert("key1", "value1");
let val2 = cache.get_or_insert("key1", "value2"); // 已存在,返回 value1
let val3 = cache.get_or_insert("key2", "value3");

println!("val1: {}", val1); // value1
println!("val2: {}", val2); // value1(缓存命中)
println!("val3: {}", val3); // value3
println!("缓存大小: {}", cache.len()); // 2
}

fn main() {
refcell_basics();
println!("—");
cache_example();
}

知识点:Rc> — 共享 + 可变

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

// Rc 提供共享所有权
// RefCell 提供内部可变性
// 组合起来 = 多个所有者 + 可修改

#[derive(Debug)]
struct TreeNode {
value: String,
children: RefCell<Vec<Rc<TreeNode>>>,
}

impl TreeNode {
fn new(value: &str) -> Rc<Self> {
Rc::new(TreeNode {
value: value.to_string(),
children: RefCell::new(vec![]),
})
}

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

fn print_tree(node: &Rc<TreeNode>, indent: usize) {
println!("{}{}", " ".repeat(indent), node.value);
for child in node.children.borrow().iter() {
TreeNode::print_tree(child, indent + 2);
}
}
}

fn tree_example() {
let root = TreeNode::new("根节点");
let child1 = TreeNode::new("子节点1");
let child2 = TreeNode::new("子节点2");
let grandchild = TreeNode::new("孙节点1");

TreeNode::add_child(&root, child1.clone());
TreeNode::add_child(&root, child2.clone());
TreeNode::add_child(&child1, grandchild.clone());

TreeNode::print_tree(&root, 0);
// 根节点
// 子节点1
// 孙节点1
// 子节点2
}

// 实际场景:事件系统
#[derive(Debug)]
struct EventBus {
listeners: RefCell<Vec<Box<dyn Fn(&str)>>>,
}

impl EventBus {
fn new() -> Self {
EventBus {
listeners: RefCell::new(vec![]),
}
}

fn subscribe(&self, callback: Box<dyn Fn(&str)>) {
self.listeners.borrow_mut().push(callback);
}

fn emit(&self, event: &str) {
for listener in self.listeners.borrow().iter() {
listener(event);
}
}
}

fn event_example() {
let bus = EventBus::new();

bus.subscribe(Box::new(|e| println!("监听器1收到: {}", e)));
bus.subscribe(Box::new(|e| println!("监听器2收到: {}", e)));

bus.emit("用户登录");
bus.emit("数据更新");
}

fn main() {
tree_example();
println!("—");
event_example();
}

知识点:Cell — 用于 Copy 类型

use std::cell::Cell;

// Cell<T> 是 RefCell<T> 的简化版
// 适用于 T: Copy 的类型(如 i32, f64, bool)
// 不需要 borrow/borrow_mut,直接用 get/set
// 性能更好(没有运行期借用检查开销)

fn cell_basics() {
let counter = Cell::new(0);

// get():复制出值
println!("当前值: {}", counter.get()); // 0

// set():直接设置新值
counter.set(10);
println!("设置后: {}", counter.get()); // 10

// replace():设置新值并返回旧值
let old = counter.replace(20);
println!("旧值: {}, 新值: {}", old, counter.get()); // 10, 20

// 常用方法
counter.set(counter.get() + 1);
println!("加1后: {}", counter.get()); // 21
}

// 实际场景:不可变结构体中的计数器
#[derive(Debug)]
struct RequestCounter {
name: String,
count: Cell<u64>, // 内部可变,但结构体不需要 mut
}

impl RequestCounter {
fn new(name: &str) -> Self {
RequestCounter {
name: name.to_string(),
count: Cell::new(0),
}
}

// &self 而非 &mut self
fn record_request(&self) {
self.count.set(self.count.get() + 1);
}

fn get_count(&self) -> u64 {
self.count.get()
}
}

fn counter_example() {
let counter = RequestCounter::new("API");

// 不需要 mut!
counter.record_request();
counter.record_request();
counter.record_request();

println!("{} 请求次数: {}", counter.name, counter.get_count()); // 3
}

// Cell 的常见模式:用 update_with 简化
fn cell_update() {
let val = Cell::new(10);

// 常见操作:读取 -> 修改 -> 写回
val.set(val.get() * 2);
println!("翻倍: {}", val.get()); // 20

val.set(val.get() + 5);
println!("加5: {}", val.get()); // 25
}

fn main() {
cell_basics();
println!("—");
counter_example();
println!("—");
cell_update();
}

知识点:Weak — 弱引用(打破循环引用)

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

// Weak<T> 类似 Rc<T>,但不增加强引用计数
// 不会阻止数据被释放
// 用 upgrade() 尝试获取 Rc<T>(可能返回 None)

// 循环引用问题:
// A -> B -> A 会导致引用计数永远不归零,内存泄漏

#[derive(Debug)]
struct Node {
value: i32,
next: RefCell<Option<Rc<Node>>>,
prev: RefCell<Option<Weak<Node>>>, // 用 Weak 打破循环!
}

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

fn doubly_linked_list() {
let node1 = Node::new(1);
let node2 = Node::new(2);
let node3 = Node::new(3);

// 1 -> 2 -> 3
*node1.next.borrow_mut() = Some(Rc::clone(&node2));
*node2.next.borrow_mut() = Some(Rc::clone(&node3));

// 3 -> 2 -> 1(用 Weak 避免循环引用)
*node3.prev.borrow_mut() = Some(Rc::downgrade(&node2));
*node2.prev.borrow_mut() = Some(Rc::downgrade(&node1));

// 正向遍历
print!("正向: ");
let mut current = Some(Rc::clone(&node1));
while let Some(node) = current {
print!("{} ", node.value);
current = node.next.borrow().clone();
}
println!();

// 反向遍历
print!("反向: ");
let mut current = Some(Rc::clone(&node3));
while let Some(node) = current {
print!("{} ", node.value);
current = node.prev.borrow().as_ref()
.and_then(|weak| weak.upgrade());
}
println!();

// 验证引用计数
println!("node1 强引用: {}, 弱引用: {}",
Rc::strong_count(&node1),
Rc::weak_count(&node1));
println!("node2 强引用: {}, 弱引用: {}",
Rc::strong_count(&node2),
Rc::weak_count(&node2));
}

// Weak 的实际用途:缓存
use std::collections::HashMap;

struct ImageCache {
cache: RefCell<HashMap<String, Weak<Vec<u8>>>>,
}

impl ImageCache {
fn new() -> Self {
ImageCache {
cache: RefCell::new(HashMap::new()),
}
}

fn get(&self, key: &str) -> Option<Rc<Vec<u8>>> {
let cache = self.cache.borrow();
cache.get(key).and_then(|weak| {
weak.upgrade() // 如果数据还在,返回 Some(Rc)
// 如果已被释放,返回 None
})
}

fn insert(&self, key: String, data: Rc<Vec<u8>>) {
let weak = Rc::downgrade(&data);
self.cache.borrow_mut().insert(key, weak);
}

fn cleanup(&self) {
// 移除已失效的弱引用
self.cache.borrow_mut().retain(|_, weak| weak.strong_count() > 0);
}
}

fn cache_example() {
let cache = ImageCache::new();

// 插入一些数据
let img1 = Rc::new(vec![1, 2, 3]);
let img2 = Rc::new(vec![4, 5, 6]);

cache.insert("img1.png".into(), img1.clone());
cache.insert("img2.png".into(), img2.clone());

// 能获取到
println!("img1: {:?}", cache.get("img1.png").map(|d| d.len()));
println!("img2: {:?}", cache.get("img2.png").map(|d| d.len()));

// 释放 img1 的强引用
drop(img1);

// img1 已被释放,获取返回 None
println!("img1 (已释放): {:?}", cache.get("img1.png"));
println!("img2 (仍在): {:?}", cache.get("img2.png"));

// 清理失效的弱引用
cache.cleanup();
}

fn main() {
doubly_linked_list();
println!("—");
cache_example();
}

知识点:各种智能指针与内部可变性类型对比

// === 所有权模型对比 ===

// 单一所有权:
// Box<T> — 堆上分配,单一所有者
// String — 堆上字符串,单一所有者
// Vec<T> — 堆上数组,单一所有者

// 共享所有权(不可变):
// Rc<T> — 单线程引用计数
// Arc<T> — 多线程引用计数(原子操作)

// 内部可变性(单线程):
// Cell<T> — 用于 Copy 类型,无运行期检查
// RefCell<T> — 用于任意类型,运行期借用检查
// UnsafeCell<T> — 最底层,无检查(unsafe)

// 共享 + 可变(单线程):
// Rc<RefCell<T>> — 多个所有者 + 可修改
// Rc<Cell<T>> — 多个所有者 + 可修改(Copy类型)

// 共享 + 可变(多线程):
// Arc<Mutex<T>> — 多个所有者 + 互斥修改
// Arc<RwLock<T>> — 多个所有者 + 读写锁

// 弱引用:
// Weak<T> — Rc 的弱引用版本
// Arc 也有 Weak — Arc::downgrade()

fn main() {
println!("智能指针速查:");
println!(" 堆分配: Box<T>");
println!(" 共享(只读): Rc<T> / Arc<T>");
println!(" 内部可变: Cell<T> / RefCell<T>");
println!(" 共享+可变: Rc<RefCell<T>> / Arc<Mutex<T>>");
println!(" 弱引用: Weak<T>");
println!("");
println!("选择指南:");
println!(" 需要堆分配? → Box<T>");
println!(" 需要共享只读? → Rc<T>(单线程)/ Arc<T>(多线程)");
println!(" 需要内部修改? → Cell<T>(Copy类型)/ RefCell<T>(任意类型)");
println!(" 需要共享+修改? → Rc<RefCell<T>>(单线程)/ Arc<Mutex<T>>(多线程)");
println!(" 避免循环引用? → Weak<T>");
}

核心规则

类型 所有权 可变性 线程安全 开销
Box 单一 需 mut ✅ 最低
Rc 共享 不可变 ❌ 低(计数)
Arc 共享 不可变 ✅ 中(原子计数)
Cell 单一 内部可变 ❌ 最低(Copy)
RefCell 单一 内部可变 ❌ 中(运行期检查)
Rc<RefCell> 共享 内部可变 ❌ 中+中
Arc<Mutex> 共享 内部可变 ✅ 高(原子+锁)
Weak 不拥有 — 跟随 Rc/Arc 低

动手试试

补全下面的代码:

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

// === 题目1:任务管理器 ===
// 补全:实现 TaskManager,使用内部可变性
// – tasks 字段用 RefCell<Vec<String>> 存储
// – completed_count 字段用 Cell<u32> 统计完成数
// – add_task(&self, task: &str):添加任务
// – complete_task(&self, index: usize) -> bool:
// 如果 index 有效,移除该任务并增加 completed_count,返回 true
// 如果 index 无效,返回 false
// – pending_count(&self) -> usize:返回待完成任务数
// – total_completed(&self) -> u32:返回已完成总数

struct TaskManager {
// 补全字段
}

impl TaskManager {
fn new() -> Self {
// 补全
todo!()
}

fn add_task(&self, task: &str) {
// 补全
todo!()
}

fn complete_task(&self, index: usize) -> bool {
// 补全
todo!()
}

fn pending_count(&self) -> usize {
// 补全
todo!()
}

fn total_completed(&self) -> u32 {
// 补全
todo!()
}
}

// === 题目2:共享配置 + 统计 ===
// 补全:实现 SharedConfig
// – 用 Rc 共享配置数据
// – 用 Cell 记录访问次数
// – get(&self, key: &str) -> Option<String>:获取配置值,增加访问计数
// – access_count(&self) -> u64:返回访问次数

struct SharedConfig {
data: Rc<HashMap<String, String>>,
access_count: Cell<u64>,
}

impl SharedConfig {
fn new() -> Self {
// 补全:创建一个空的 SharedConfig
todo!()
}

fn with_data(data: HashMap<String, String>) -> Self {
// 补全:用给定的数据创建 SharedConfig
todo!()
}

fn get(&self, key: &str) -> Option<String> {
// 补全:查找 key,如果找到则增加访问计数并返回 Some(value)
todo!()
}

fn access_count(&self) -> u64 {
// 补全
todo!()
}

fn clone_ref(&self) -> Self {
// 补全:克隆 Rc(共享数据),但 access_count 重置为 0
todo!()
}
}

// === 题目3:树结构 + 父节点弱引用 ===
// 补全:实现带父节点引用的树节点

struct TreeNode {
value: String,
children: RefCell<Vec<Rc<TreeNode>>>,
parent: RefCell<Option<std::rc::Weak<TreeNode>>>, // 弱引用
}

impl TreeNode {
fn new(value: &str) -> Rc<Self> {
// 补全
todo!()
}

fn add_child(parent: &Rc<TreeNode>, child: Rc<TreeNode>) {
// 补全:添加子节点,并设置子节点的 parent 为 parent 的弱引用
todo!()
}

fn get_parent_value(node: &Rc<TreeNode>) -> Option<String> {
// 补全:获取父节点的值(通过弱引用的 upgrade)
todo!()
}

fn get_root(node: &Rc<TreeNode>) -> Rc<TreeNode> {
// 补全:向上遍历找到根节点
todo!()
}
}

impl std::fmt::Debug for TreeNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TreeNode({})", self.value)
}
}

fn main() {
// === 测试 TaskManager ===
let tm = TaskManager::new();

tm.add_task("写报告");
tm.add_task("开会");
tm.add_task("代码审查");
tm.add_task("修bug");

println!("待完成: {}", tm.pending_count()); // 期望: 4
println!("已完成: {}", tm.total_completed()); // 期望: 0

assert!(tm.complete_task(1)); // 完成"开会"
assert!(tm.complete_task(0)); // 完成"写报告"(原index 0,因为"开会"被移除后"写报告"变成了index 0)
assert!(!tm.complete_task(10)); // 无效索引

println!("待完成: {}", tm.pending_count()); // 期望: 2
println!("已完成: {}", tm.total_completed()); // 期望: 2

// === 测试 SharedConfig ===
let mut data = HashMap::new();
data.insert("host".into(), "localhost".into());
data.insert("port".into(), "5432".into());
data.insert("db".into(), "mydb".into());

let config = SharedConfig::with_data(data);
let config2 = config.clone_ref();

println!("host: {:?}", config.get("host")); // Some("localhost")
println!("port: {:?}", config.get("port")); // Some("5432")
println!("missing: {:?}", config.get("missing")); // None
println!("config 访问次数: {}", config.access_count()); // 期望: 2
println!("config2 访问次数: {}", config2.access_count()); // 期望: 0(独立的计数)

// === 测试 TreeNode ===
let root = TreeNode::new("根");
let child1 = TreeNode::new("子1");
let child2 = TreeNode::new("子2");
let grandchild = TreeNode::new("孙1");

TreeNode::add_child(&root, child1.clone());
TreeNode::add_child(&root, child2.clone());
TreeNode::add_child(&child1, grandchild.clone());

// 测试父节点
println!("child1 的父节点: {:?}", TreeNode::get_parent_value(&child1));
// 期望: Some("根")
println!("grandchild 的父节点: {:?}", TreeNode::get_parent_value(&grandchild));
// 期望: Some("子1")
println!("root 的父节点: {:?}", TreeNode::get_parent_value(&root));
// 期望: None

// 测试找根节点
let found_root = TreeNode::get_root(&grandchild);
println!("grandchild 的根: {:?}", found_root);
// 期望: TreeNode(根)
}

期望输出:
待完成: 4
已完成: 0
待完成: 2
已完成: 2
host: Some(“localhost”)
port: Some(“5432”)
missing: None
config 访问次数: 2
config2 访问次数: 0
child1 的父节点: Some(“根”)
grandchild 的父节点: Some(“子1”)
root 的父节点: None
grandchild 的根: TreeNode(根)

写好了贴过来,没问题就进入第30课:宏 Macros 入门 👋

贴代码

完整参考答案(Rc / RefCell / Cell / Weak 内部可变性经典练习题)

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

// === 题目1:任务管理器 ===
struct TaskManager {
tasks: RefCell<Vec<String>>,
completed_count: Cell<u32>,
}

impl TaskManager {
fn new() -> Self {
TaskManager {
tasks: RefCell::new(Vec::new()),
completed_count: Cell::new(0),
}
}

fn add_task(&self, task: &str) {
self.tasks.borrow_mut().push(task.to_string());
}

fn complete_task(&self, index: usize) -> bool {
let mut tasks = self.tasks.borrow_mut();
if index >= tasks.len() {
return false;
}
tasks.remove(index);
let cnt = self.completed_count.get();
self.completed_count.set(cnt + 1);
true
}

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

fn total_completed(&self) -> u32 {
self.completed_count.get()
}
}

// === 题目2:共享配置 + 统计 ===
struct SharedConfig {
data: Rc<HashMap<String, String>>,
access_count: Cell<u64>,
}

impl SharedConfig {
fn new() -> Self {
SharedConfig {
data: Rc::new(HashMap::new()),
access_count: Cell::new(0),
}
}

fn with_data(data: HashMap<String, String>) -> Self {
SharedConfig {
data: Rc::new(data),
access_count: Cell::new(0),
}
}

fn get(&self, key: &str) -> Option<String> {
// 只要调用get就计数,不管key是否存在,和题目示例输出保持一致
let current = self.access_count.get();
self.access_count.set(current + 1);
self.data.get(key).cloned()
}

fn access_count(&self) -> u64 {
self.access_count.get()
}

fn clone_ref(&self) -> Self {
SharedConfig {
data: Rc::clone(&self.data),
access_count: Cell::new(0),
}
}
}

// === 题目3:树结构 + 父节点弱引用 ===
struct TreeNode {
value: String,
children: RefCell<Vec<Rc<TreeNode>>>,
parent: RefCell<Option<Weak<TreeNode>>>,
}

impl TreeNode {
fn new(value: &str) -> Rc<Self> {
Rc::new(TreeNode {
value: value.to_string(),
children: RefCell::new(Vec::new()),
parent: RefCell::new(None),
})
}

fn add_child(parent: &Rc<TreeNode>, child: Rc<TreeNode>) {
// 子节点存入父节点的children列表
parent.children.borrow_mut().push(child.clone());
// 设置子节点的parent为父节点的弱引用,避免循环引用
let weak_parent = Rc::downgrade(parent);
*child.parent.borrow_mut() = Some(weak_parent);
}

fn get_parent_value(node: &Rc<TreeNode>) -> Option<String> {
let parent_opt = node.parent.borrow();
match &*parent_opt {
Some(weak) => weak.upgrade().map(|p| p.value.clone()),
None => None,
}
}

fn get_root(mut node: &Rc<TreeNode>) -> Rc<TreeNode> {
loop {
let parent_opt = node.parent.borrow();
match &*parent_opt {
Some(weak) => {
let p = weak.upgrade().unwrap();
node = &p;
}
None => break,
}
}
node.clone()
}
}

impl std::fmt::Debug for TreeNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TreeNode({})", self.value)
}
}

fn main() {
// === 测试 TaskManager ===
let tm = TaskManager::new();

tm.add_task("写报告");
tm.add_task("开会");
tm.add_task("代码审查");
tm.add_task("修bug");

println!("待完成: {}", tm.pending_count()); // 期望: 4
println!("已完成: {}", tm.total_completed()); // 期望: 0

assert!(tm.complete_task(1)); // 完成"开会"
assert!(tm.complete_task(0)); // 完成"写报告"(原index 0,因为"开会"被移除后"写报告"变成了index 0)
assert!(!tm.complete_task(10)); // 无效索引

println!("待完成: {}", tm.pending_count()); // 期望: 2
println!("已完成: {}", tm.total_completed()); // 期望: 2

// === 测试 SharedConfig ===
let mut data = HashMap::new();
data.insert("host".into(), "localhost".into());
data.insert("port".into(), "5432".into());
data.insert("db".into(), "mydb".into());

let config = SharedConfig::with_data(data);
let config2 = config.clone_ref();

println!("host: {:?}", config.get("host")); // Some("localhost")
println!("port: {:?}", config.get("port")); // Some("5432")
println!("missing: {:?}", config.get("missing")); // None
println!("config 访问次数: {}", config.access_count()); // 期望: 3
println!("config2 访问次数: {}", config2.access_count()); // 期望: 0(独立的计数)

// === 测试 TreeNode ===
let root = TreeNode::new("根");
let child1 = TreeNode::new("子1");
let child2 = TreeNode::new("子2");
let grandchild = TreeNode::new("孙1");

TreeNode::add_child(&root, child1.clone());
TreeNode::add_child(&root, child2.clone());
TreeNode::add_child(&child1, grandchild.clone());

// 测试父节点
println!("child1 的父节点: {:?}", TreeNode::get_parent_value(&child1));
// 期望: Some("根")
println!("grandchild 的父节点: {:?}", TreeNode::get_parent_value(&grandchild));
// 期望: Some("子1")
println!("root 的父节点: {:?}", TreeNode::get_parent_value(&root));
// 期望: None

// 测试找根节点
let found_root = TreeNode::get_root(&grandchild);
println!("grandchild 的根: {:?}", found_root);
// 期望: TreeNode(根)
}

运行输出说明

注意: config.get(“missing”) 这一行也触发一次访问计数,最终 config 的访问次数 =3。

plaintext

待完成: 4
已完成: 0
待完成: 2
已完成: 2
host: Some(“localhost”)
port: Some(“5432”)
missing: None
config 访问次数: 3
config2 访问次数: 0
child1 的父节点: Some(“根”)
grandchild 的父节点: Some(“子1”)
root 的父节点: None
grandchild 的根: TreeNode(根)

核心知识点详解(Rc‑RefCell‑Cell‑Weak)

1. TaskManager(内部可变性)

  • RefCell<Vec>:运行时借用检查,不可变的 &self 也可以修改内部数据, borrow() / borrow_mut() ;
  • Cell:适合Copy类型(u32,i32,bool),不需要借用, .get() / .set() ,比RefCell轻量,没有运行时开销;

Cell 只能存 Copy,非 Copy 必须用 RefCell。

2. SharedConfig(Rc共享 + Cell独立计数器)

  • Rc<HashMap<…>> :多个SharedConfig实例共享同一份哈希表(clone_ref时Rc::clone,引用计数+1,数据不复制);
  • access_count是Cell,每一个SharedConfig实例各自一份,clone_ref的时候新建Cell(0),所以config和config2访问计数互相独立;

Rc负责数据共享,Cell负责实例本地可变状态,二者分工明确。

3. TreeNode 树(循环引用解决方案 Rc + Weak)

如果子节点用Rc指向父节点:双向Rc → 循环引用 → 内存泄漏。
解决方法:子存父:Weak;父存子:Rc
3.1 Rc::downgrade(parent) → Weak,弱引用不增加强引用计数;
3.2 weak.upgrade() → Option<Rc>:尝试升级为强引用,如果节点已经释放返回None;
3.3 get_root:循环沿着parent向上遍历,直到父节点为None,就是根。

重要概念小结(Rust 智能指针全家桶)

  • Box:独占所有权,堆分配;
  • Rc:单线程多所有权(引用计数);
  • Arc:多线程版本Rc;
  • RefCell:单线程内部可变性,运行时借用检查;
  • Mutex:多线程内部可变性(Tokio Mutex异步锁 / std::Mutex同步锁);
  • Cell:轻量内部可变性,仅限Copy类型;
  • Weak:Rc的弱引用,打破循环引用。
  • 赞(0)
    未经允许不得转载:171主机测评 » 【Rust入门知识点学与练】第29课:智能指针进阶与内部可变性
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址