Rc 和 Arc

Rust 建立在所有權之上的這一套機制,它要求一個資源同一時刻有且只能有一個擁有所有權的綁定或 &mut 引用,這在大部分的情況下保證了內存的安全。但是這樣的設計是相當嚴格的,在另外一些情況下,它限制了程序的書寫,無法實現某些功能。因此,Rust 在 std 庫中提供了額外的措施來補充所有權機制,以應對更廣泛的場景。

默認 Rust 中,對一個資源,同一時刻,有且只有一個所有權擁有者。RcArc 使用引用計數的方法,讓程序在同一時刻,實現同一資源的多個所有權擁有者,多個擁有者共享資源。

Rc

Rc 用於同一線程內部,通過 use std::rc::Rc 來引入。它有以下幾個特點:

  1. Rc 包裝起來的類型對象,是 immutable 的,即 不可變的。即你無法修改 Rc<T> 中的 T 對象,只能讀;
  2. 一旦最後一個擁有者消失,則資源會被自動回收,這個生命週期是在編譯期就確定下來的;
  3. Rc 只能用於同一線程內部,不能用於線程之間的對象共享(不能跨線程傳遞);
  4. Rc 實際上是一個指針,它不影響包裹對象的方法調用形式(即不存在先解開包裹再調用值這一說)。

例子:

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 版。它有以下幾個特點:

  1. 可訪問,但不擁有。不增加引用計數,因此,不會對資源回收管理造成影響;
  2. 可由 Rc<T> 調用 downgrade 方法而轉換成 Weak<T>
  3. Weak<T> 可以使用 upgrade 方法轉換成 Option<Rc<T>>,如果資源已經被釋放,則 Option 值為 None
  4. 常用於解決循環引用的問題。

例子:

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 引入。

它的特點:

  1. Arc 可跨線程傳遞,用於跨線程共享一個對象;
  2. Arc 包裹起來的類型對象,對可變性沒有要求;
  3. 一旦最後一個擁有者消失,則資源會被自動回收,這個生命週期是在編譯期就確定下來的;
  4. Arc 實際上是一個指針,它不影響包裹對象的方法調用形式(即不存在先解開包裹再調用值這一說);
  5. 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.
}

results matching ""

    No results matching ""