iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
Rust

Rust 30 天養成計畫:從零到 CLI 專案系列 第 29

Day 29:RefCell<T> 與內部可變性(Interior Mutability)

  • 分享至 

  • xImage
  •  

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 的型別系統又多了一層信任。


上一篇
Day 28:Arc<T> 與多執行緒共享資料
下一篇
Day 30:小專案 — 可互動任務清單
系列文
Rust 30 天養成計畫:從零到 CLI 專案30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言