智慧指標
智慧指標(smart pointer)是「行為像指標,但還帶有額外功能與所有權語意」的型別。Vec 與 String 其實就是智慧指標。本頁介紹三個最常用、彼此分工不同的型別:Box<T>、Rc<T> 與 RefCell<T>。
三者的分工
| 型別 | 用途 | 所有者數量 | 借用規則檢查時機 |
|---|---|---|---|
Box<T> |
把資料放到堆積上 | 一個 | 編譯時 |
Rc<T> |
讓同一份資料有多個擁有者(單執行緒) | 多個 | 編譯時(只能讀) |
RefCell<T> |
允許透過不可變的外殼修改內部資料 | 一個 | 執行時 |
Box:把資料放到堆積
Box<T> 是最簡單的智慧指標:資料存在堆積,堆疊上只放一個指標。最常見的用途是遞迴型別,因為編譯器需要在編譯時知道型別大小,而遞迴型別沒有辦法直接算出來:
用 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」一節。
下一頁:並行。