Rust 智能指针

`Box`、`Rc`、`RefCell`、`Weak`,以及内部可变性。

Advanced
Key Points`Box` 独占堆内存,`Rc` 共享只读,`RefCell` 把借用检查推迟到运行时。

Box

Rust
fn main() {
    let b = Box::new(5);
    println!("{}", b);
    println!("{}", *b);          // 解引用

    // 递归类型必须用 Box(否则大小无限)
    #[derive(Debug)]
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }

    let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
    println!("{:?}", list);
}
Output
5
5
Cons(1, Cons(2, Nil))

enum List { Cons(i32, List) } fails: the compiler cannot size List. With Box the size is known (one pointer) — the one use of Box with no substitute.

Rc

Rust
use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("共享数据"));
    println!("计数 {}", Rc::strong_count(&a));

    let b = Rc::clone(&a);       // 只增计数,不复制数据
    println!("计数 {}", Rc::strong_count(&a));

    {
        let c = Rc::clone(&a);
        println!("计数 {}", Rc::strong_count(&a));
    }
    println!("计数 {}", Rc::strong_count(&a));
}
Output
计数 1
计数 2
计数 3
计数 2

Rc::clone does not copy data, it increments the count — unlike a.clone() on a String, which deep-copies. The Rc:: prefix exists to signal this clone is cheap.

RefCell

Rust
use std::cell::RefCell;

fn main() {
    let c = RefCell::new(5);

    *c.borrow_mut() += 1;
    println!("{}", c.borrow());

    // 运行时检查借用规则
    let b1 = c.borrow();
    let b2 = c.borrow();
    println!("{} {}", b1, b2);
    drop(b1);
    drop(b2);

    // let m = c.borrow_mut();  // 已有不可变借用时会 panic
}
Output
6
6 6

RefCell moves borrow checking from compile time to runtime: violations panic instead of failing the build. It enables mutating through an immutable reference — interior mutability.

RefCell panics at runtime, possibly in production. Use it only when the compiler genuinely cannot prove what you know is safe, and keep borrow() scopes tiny.

Rc<RefCell<T>>

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

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

fn main() {
    let leaf = Rc::new(RefCell::new(Node { value: 2, children: vec![] }));
    let root = Rc::new(RefCell::new(Node {
        value: 1,
        children: vec![Rc::clone(&leaf)],
    }));

    root.borrow_mut().children.push(Rc::new(RefCell::new(
        Node { value: 3, children: vec![] },
    )));

    println!("{}", root.borrow().children.len());
    println!("{}", leaf.borrow().value);
}
Output
2
2

Rc<RefCell<T>> gives multiple owners plus mutation on one thread — common for graphs, trees and observers. It is **not Send**; use Arc<Mutex<T>> across threads.

Weak 打破循环引用

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

struct Node {
    name: String,
    parent: RefCell<Weak<Node>>,     // 弱引用,不增计数
}

fn main() {
    let parent = Rc::new(Node {
        name: "父".into(),
        parent: RefCell::new(Weak::new()),
    });

    let child = Rc::new(Node {
        name: "子".into(),
        parent: RefCell::new(Rc::downgrade(&parent)),
    });

    println!("父计数 {}", Rc::strong_count(&parent));
    println!("{}", child.parent.borrow().upgrade().unwrap().name);
}
Output
父计数 1
父

Two Rcs pointing at each other never reach zero and leak. Make the back edge a Weak (from Rc::downgrade) so it does not count as a strong reference and the cycle breaks.

选哪个

NeedUse
只是放到堆上Box<T>
多个所有者,只读(单线程)Rc<T>
多个所有者 + 可改(单线程)Rc<RefCell<T>>
多个所有者(多线程)Arc<T>
多个所有者 + 可改(多线程)Arc<Mutex<T>>
避免循环引用Weak<T>

Friends & Partners