跳轉至

建立 2026-09-19 更新 2026-09-19

智慧指標

智慧指標(smart pointer)是「行為像指標,但還帶有額外功能與所有權語意」的型別。VecString 其實就是智慧指標。本頁介紹三個最常用、彼此分工不同的型別:Box<T>Rc<T>RefCell<T>

三者的分工

型別 用途 所有者數量 借用規則檢查時機
Box<T> 把資料放到堆積上 一個 編譯時
Rc<T> 讓同一份資料有多個擁有者(單執行緒) 多個 編譯時(只能讀)
RefCell<T> 允許透過不可變的外殼修改內部資料 一個 執行時

Box:把資料放到堆積

Box<T> 是最簡單的智慧指標:資料存在堆積,堆疊上只放一個指標。最常見的用途是遞迴型別,因為編譯器需要在編譯時知道型別大小,而遞迴型別沒有辦法直接算出來:

// ❌ 無法編譯:遞迴型別的大小無限大
enum List {
    Cons(i32, List),
    Nil,
}

fn main() {}

Box 包起來,指標的大小是固定的,就能解決:

enum List {
    Cons(i32, Box<List>),
    Nil,
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));

    let mut current = &list;
    while let Cons(value, next) = current {
        println!("{value}");
        current = next;
    }
}

Box 也常搭配 dyn Trait 使用(見 泛型與 Trait),離開作用域時會自動釋放堆積上的資料。

Rc:多個擁有者

有時一份資料需要被多處共同擁有,例如圖形結構中的節點被多個節點指向。Rc<T>(reference counting,參照計數)會記錄有幾個擁有者,最後一個離開時才釋放資料:

use std::rc::Rc;

fn main() {
    let shared = Rc::new(String::from("共享的資料"));
    println!("計數:{}", Rc::strong_count(&shared)); // 1

    let a = Rc::clone(&shared); // 只增加計數,不複製資料
    {
        let b = Rc::clone(&shared);
        println!("計數:{}", Rc::strong_count(&shared)); // 3
        println!("{b}");
    } // b 離開作用域,計數減一
    println!("計數:{}", Rc::strong_count(&shared)); // 2
    println!("{a}");
}

注意:

  • Rc<T> 只能提供唯讀存取。
  • 它只能用在單一執行緒。跨執行緒要改用 Arc<T>,見 並行

RefCell:內部可變性

Rust 的借用規則通常在編譯時檢查,但有些情況編譯器無法證明程式是安全的。RefCell<T> 提供內部可變性(interior mutability):即使外面是不可變的參照,也能修改裡面的資料,而借用規則改成在執行時檢查,違反時會 panic:

use std::cell::RefCell;

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

    data.borrow_mut().push(4); // 取得可修改借用
    println!("{:?}", data.borrow()); // 取得唯讀借用
}

Rc 與 RefCell 組合

Rc<RefCell<T>> 是常見的組合:多個擁有者,而且都能修改

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

fn main() {
    let counter = Rc::new(RefCell::new(0));

    let a = Rc::clone(&counter);
    let b = Rc::clone(&counter);

    *a.borrow_mut() += 1;
    *b.borrow_mut() += 10;

    println!("{}", counter.borrow()); // 11
}

不要濫用

Rc<RefCell<T>> 把編譯時的檢查延到執行時,用錯會 panic。同時,Rc 互相參照會造成循環參照(reference cycle),導致記憶體永遠不釋放。大多數情況,先考慮用普通的所有權與借用設計,不夠用時再引入它們。

推薦影音

Box

簡述:Let's Get Rusty 依照官方書籍第 15 章製作,介紹 Box<T> 的用途與遞迴型別。

Rc 參照計數

簡述:同一系列的 Rc<T> 單元,說明多重擁有者與參照計數,對應本頁「Rc」一節。

RefCell 內部可變性

簡述:同一系列的 RefCell<T> 單元,說明內部可變性與執行時的借用檢查,對應本頁「RefCell」一節。

下一頁:並行