Rust 筆記:Unique 和 NonNull

Unique 是 Rust 中的一種指針類型,通常在 Rust 內部使用它來表示唯一所有權語義,對於構建像 Box、Vec、String 和 HashMap<K, V > 這樣的抽象非常有用。

它被定義在標準庫的 core/src/ptr/unique.rs 中:

pub struct Unique<T: ?Sized> {
    pointer: NonNull<T>,
    _marker: PhantomData<T>,
}

Unique 是非空的、協變的、唯一的指針。以下是這些術語的含義:

這些屬性的組合允許 Rust 做出某些可以在代碼中進行優化的假設。例如,因爲 Unique 指針是非空且唯一的,Rust 的優化器可以假設對 Unique 的解引用總是安全的,並且該指針不會被代碼的任何其他部分修改或移動。

請注意 Unique 是 Rust 標準庫的內部實現。因此,你通常不會在自己的代碼中直接使用這種類型。相反,你應該使用 Box、Vec 等類型,它們是在幕後使用 Unique 構建的。

例如,Box 在標準庫中的定義如下:

pub struct Box<
    T: ?Sized,
    #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
>(Unique<T>, A);

PhantomData

如上所示,Unique 有一個類型爲 PhantomData 的字段_marker。

Rust 中的 PhantomData 是一種類型,用於指示特定的泛型 T 是數據結構的一部分,即使 T 沒有在數據結構中使用。

PhantomData 在 Rust 編譯器理解所有權、借用和生命週期的能力中起着至關重要的作用,在創建複雜的數據結構或處理原始指針時非常重要。

在 Unique 結構的聲明中,PhantomData 表示它 “擁有” 一個 T,即使 T 實際上並沒有在結構中使用。這告訴 Rust 編譯器,當一個 Unique 被刪除時,它可能會刪除一個 T,所以它不應該允許之後使用這個 T。

如果沒有 PhantomData, Rust 的類型系統將無法理解 Unique 擁有一個 T,這可能會導致未定義的行爲。

總之,在 Rust 中,PhantomData 和 Unique 經常一起使用來表達複雜的所有權關係,這些關係是 Rust 的類型系統無法單獨表達的。

NonNull

Unique 不直接存儲原始指針。相反,它有一個 NonNull 類型的指針字段。

在 Rust 中,NonNull 是保證不爲 NULL 的原始指針的包裝器。它本質上和 * mut 是一樣的,只是增加了一個不爲 null 的不變量。

Rust 中的原始指針通常在與 C 代碼交互或構建不安全的 Rust 抽象時使用。然而,空指針經常會導致錯誤和崩潰,因此提供 NonNull 作爲一種確保原始指針永遠不會爲 null 的方法。

這提供了一些安全保證,因爲你不必在使用它之前檢查 null。但是請記住,即使它不能爲空,使用 NonNull 仍然是不安全的,因爲它不提供任何引用 (&T 和 & mut) 所提供的借用或生命週期保證。

下面是一個如何使用 NonNull 的例子:

use std::ptr::NonNull;

let mut value = 10;
let pointer = NonNull::new(&mut value as *mut _); 
if let Some(ptr) = pointer {
    unsafe {
        *ptr.as_ptr() = 20;
    }
    assert_eq!(value, 20);
}

在本例中,NonNull::new 用於創建一個新的 NonNull。然後,使用 NonNull::as_ptr 獲取原始指針,該指針可以在 unsafe 塊中解引用和修改。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/wRdQS4OLgttgAxFtN8UNQw