iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
Rust

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

Day 27:Rc<T> 與多重擁有權(Reference Counting)

  • 分享至 

  • xImage
  •  

1. 為什麼需要 Rc
在 Rust 的所有權規則中,一個值同一時間只能有一個擁有者。但有時候,我們希望能讓多個變數共同擁有一份資料,像多個節點同時指向同一段記憶體的情況,這時候就需要 Rc(Reference Counted 智慧指標)。
Rc 透過「引用計數」來追蹤資料被多少個擁有者使用,當最後一個擁有者離開作用域時,記憶體才會被釋放。但 Rc 僅能在單執行緒(single-thread)環境下使用,如果是多執行緒就要用 Arc。

2. Rc 的基本使用

use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("Rust"));
    let b = Rc::clone(&a); // 增加引用計數
    let c = Rc::clone(&a);

    println!("a = {}", a);
    println!("引用數量 = {}", Rc::strong_count(&a));
}

輸出:

a = Rust
引用數量 = 3

這裡 Rc::clone(&a)不會複製資料,只會讓引用計數加一,當 a、b、c 全部離開作用域後,Rust 會自動釋放那份記憶體。

3. Rc 在結構中的應用
假設要建立一個共享節點的鏈結結構(linked list),可以用 Rc 讓多個節點共用同一個尾端。

use std::rc::Rc;

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

use List::{Cons, Nil};

fn main() {
    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
    println!("count after creating a = {}", Rc::strong_count(&a));

    let b = Cons(3, Rc::clone(&a));
    println!("count after creating b = {}", Rc::strong_count(&a));

    {
        let c = Cons(4, Rc::clone(&a));
        println!("count after creating c = {}", Rc::strong_count(&a));
    }

    println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}

輸出:

count after creating a = 1
count after creating b = 2
count after creating c = 3
count after c goes out of scope = 2

可以看到每當建立新的 Rc 引用,計數就加一,當變數離開作用域時,計數就減一,當計數歸零時,Rc 內的資料就會被釋放。

4. Rc::clone vs 常規 clone
Rc::clone() 不會 複製底層資料,只會增加引用計數,相反地,對一般型別呼叫 .clone() 會建立一份新的獨立資料,因此:

let x = Rc::new(String::from("Rust"));
let y = Rc::clone(&x); // 共用資料
let z = (*x).clone();  // 產生新字串

這樣 y 和 x 會共用同一個 heap 資料,而 z 則是完全新的複本。

5. Rc 的限制
Rc 不支援可變借用(&mut),因為這樣會導致多重擁有者同時修改資料而破壞安全性,如果需要在多重擁有權下修改資料,要搭配 RefCell 使用。

6. 學習心得與補充
學到 Rc 之後,我對 Rust 的所有權模型有了更深的理解。原本我以為一個值只能有一個擁有者的規則會讓設計變得很受限,但 Rc 讓我看到 Rust 是如何在保持安全的前提下,實現共享資料的可能。用 Rc 的時候,感覺就像在和編譯器合作,我可以有多個變數指向同一份資料,但同時又不需要擔心誰該釋放記憶體,雖然 Rc 無法修改共享資料,但我覺得這其實是一種刻意的限制,為的是讓我清楚知道什麼時候安全。我體會到Rust 所謂安全的共享到底是什麼意思,也更期待明天學到能結合 Rc 使用的 RefCell。


上一篇
Day 26:智慧指標(Smart Pointers)與 Box<T>
下一篇
Day 28:Arc<T> 與多執行緒共享資料
系列文
Rust 30 天養成計畫:從零到 CLI 專案30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言