Rc 和 Arc
Rust 建立在所有權之上的這一套機制,它要求一個資源同一時刻有且只能有一個擁有所有權的綁定或 &mut 引用,這在大部分的情況下保證了內存的安全。但是這樣的設計是相當嚴格的,在另外一些情況下,它限制了程序的書寫,無法實現某些功能。因此,Rust 在 std 庫中提供了額外的措施來補充所有權機制,以應對更廣泛的場景。
默認 Rust 中,對一個資源,同一時刻,有且只有一個所有權擁有者。Rc 和 Arc 使用引用計數的方法,讓程序在同一時刻,實現同一資源的多個所有權擁有者,多個擁有者共享資源。
Rc
Rc 用於同一線程內部,通過 use std::rc::Rc 來引入。它有以下幾個特點:
- 用
Rc包裝起來的類型對象,是immutable的,即 不可變的。即你無法修改Rc<T>中的T對象,只能讀; - 一旦最後一個擁有者消失,則資源會被自動回收,這個生命週期是在編譯期就確定下來的;
Rc只能用於同一線程內部,不能用於線程之間的對象共享(不能跨線程傳遞);Rc實際上是一個指針,它不影響包裹對象的方法調用形式(即不存在先解開包裹再調用值這一說)。
例子:
#![allow(unused)] fn main() { use std::rc::Rc; let five = Rc::new(5); let five2 = five.clone(); let five3 = five.clone(); }
Rc Weak
Weak 通過 use std::rc::Weak 來引入。
Rc 是一個引用計數指針,而 Weak 是一個指針,但不增加引用計數,是 Rc 的 weak 版。它有以下幾個特點:
- 可訪問,但不擁有。不增加引用計數,因此,不會對資源回收管理造成影響;
- 可由
Rc<T>調用downgrade方法而轉換成Weak<T>; Weak<T>可以使用upgrade方法轉換成Option<Rc<T>>,如果資源已經被釋放,則 Option 值為None;- 常用於解決循環引用的問題。
例子:
#![allow(unused)] fn main() { use std::rc::Rc; let five = Rc::new(5); let weak_five = Rc::downgrade(&five); let strong_five: Option<Rc<_>> = weak_five.upgrade(); }
Arc
Arc 是原子引用計數,是 Rc 的多線程版本。Arc 通過 std::sync::Arc 引入。
它的特點:
Arc可跨線程傳遞,用於跨線程共享一個對象;- 用
Arc包裹起來的類型對象,對可變性沒有要求; - 一旦最後一個擁有者消失,則資源會被自動回收,這個生命週期是在編譯期就確定下來的;
Arc實際上是一個指針,它不影響包裹對象的方法調用形式(即不存在先解開包裹再調用值這一說);Arc對於多線程的共享狀態幾乎是必須的(減少複製,提高性能)。
示例:
use std::sync::Arc; use std::thread; fn main() { let numbers: Vec<_> = (0..100u32).collect(); let shared_numbers = Arc::new(numbers); for _ in 0..10 { let child_numbers = shared_numbers.clone(); thread::spawn(move || { let local_numbers = &child_numbers[..]; // Work with the local numbers }); } }
Arc Weak
與 Rc 類似,Arc 也有一個對應的 Weak 類型,從 std::sync::Weak 引入。
意義與用法與 Rc Weak 基本一致,不同的點是這是多線程的版本。故不再贅述。
一個例子
下面這個例子,表述的是如何實現多個對象同時引用另外一個對象。
use std::rc::Rc; struct Owner { name: String } struct Gadget { id: i32, owner: Rc<Owner> } fn main() { // Create a reference counted Owner. let gadget_owner : Rc<Owner> = Rc::new( Owner { name: String::from("Gadget Man") } ); // Create Gadgets belonging to gadget_owner. To increment the reference // count we clone the `Rc<T>` object. let gadget1 = Gadget { id: 1, owner: gadget_owner.clone() }; let gadget2 = Gadget { id: 2, owner: gadget_owner.clone() }; drop(gadget_owner); // Despite dropping gadget_owner, we're still able to print out the name // of the Owner of the Gadgets. This is because we've only dropped the // reference count object, not the Owner it wraps. As long as there are // other `Rc<T>` objects pointing at the same Owner, it will remain // allocated. Notice that the `Rc<T>` wrapper around Gadget.owner gets // automatically dereferenced for us. println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name); println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name); // At the end of the method, gadget1 and gadget2 get destroyed, and with // them the last counted references to our Owner. Gadget Man now gets // destroyed as well. }