1. 為什麼需要 RefCell
在 Rust 中,所有的可變性通常都必須在編譯期就確定。你不能同時擁有多個可變引用(&mut),也不能在程式執行中改變一個不可變變數。但有時候,我們希望在外部看起來「不可變」,但內部仍能安全地改變狀態,這種情況下,Rust 提供了 RefCell,它允許在執行期(runtime)進行可變借用檢查,而不是編譯期。
2. RefCell 的基本用法
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
// 取得可變借用並修改
*data.borrow_mut() += 1;
// 取得不可變借用
println!("data = {}", data.borrow());
}
輸出:
data = 6
這裡 RefCell::new(5) 建立一個儲存整數的容器,borrow_mut() 回傳一個可變參考(RefMut),允許修改內容。與一般可變借用不同的是,RefCell 在執行期才會檢查借用規則。若同時有多個可變借用,就會在執行期造成 panic。
3. RefCell 的借用規則(執行期檢查)
use std::cell::RefCell;
fn main() {
let x = RefCell::new(42);
let mut a = x.borrow_mut();
let b = x.borrow_mut(); // 執行期錯誤
*a += 1;
println!("{}", b);
}
輸出:
thread 'main' panicked at 'already borrowed: BorrowMutError'
雖然程式能編譯通過,但在執行時會 panic,因為 RefCell 不允許同時多個可變借用,說明 RefCell 把借用規則從編譯期移到了執行期檢查。
4. 結合 Rc 實現可變共享
Rc 允許多重擁有,但不允許修改共享資料;RefCell 允許在執行期進行可變借用。
兩者結合,就能在多個擁有者之間共享且修改資料。
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let shared = Rc::new(RefCell::new(0));
let s1 = Rc::clone(&shared);
let s2 = Rc::clone(&shared);
*s1.borrow_mut() += 10;
*s2.borrow_mut() += 5;
println!("Final value: {}", shared.borrow());
}
輸出:
Final value: 15
這裡多個變數共用同一個資料區塊,並且都能安全修改。
5. RefCell 的限制與使用場合
RefCell 雖然方便,但若不小心同時借用多次仍可能 panic,所以應該只在確定借用時機可控的情況下使用,像是封裝在資料結構或回呼(callback)中,而不應該用來繞過所有權規則本身。
6. 學習心得與補充
以前我以為可變性只能靠 mut 來實現,但 RefCell 告訴我其實還有更靈活的做法,這種循序漸進了解更多樣化的作法的感覺讓人在學習時充滿動力,我覺得它像是一種受控的例外,讓程式在需要時可以突破編譯器的限制,但依然維持安全。當我把 Rc 和 RefCell 結合使用時,感覺就像是組裝出一個真正可共享又可變的資料結構,這讓我對 Rust 的型別系統又多了一層信任。