當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


Rust UnsafeCell用法及代碼示例


本文簡要介紹rust語言中 Struct core::cell::UnsafeCell 的用法。

用法

#[repr(transparent)]#[repr(no_niche)]pub struct UnsafeCell<T: ?Sized> { /* fields omitted */ }

Rust 內部可變性的核心原語。

如果您有引用 &T ,那麽通常在 Rust 中,編譯器會根據 &T 指向不可變數據的知識來執行優化。改變該數據,例如通過別名或將 &T 轉換為 &mut T ,被視為未定義行為。 UnsafeCell<T> opts-out 的 &T 的不變性保證:共享引用 &UnsafeCell<T> 可能指向正在發生突變的數據。這稱為“interior mutability”。

所有其他允許內部可變性的類型,例如 Cell<T>RefCell<T> ,在內部使用 UnsafeCell 來包裝它們的數據。

請注意,隻有共享引用的不變性保證受到影響UnsafeCell。可變引用的唯一性保證不受影響。有獲取別名的合法方式&mut,甚至不與UnsafeCell<T>.

UnsafeCellAPI 本身在技術上非常簡單:core::cell::UnsafeCell.get給你一個原始指針*mut T其內容。取決於作為抽象設計者正確使用原始指針。

精確的 Rust 別名規則有些變化,但要點沒有爭議:

  • 如果您創建一個生命周期為 'a 的安全引用(&T&mut T 引用),且可通過安全代碼訪問(例如,因為您返回了它),則不得以任何方式訪問數據與 'a 其餘部分的引用相矛盾。例如,這意味著如果您從 UnsafeCell<T> 中取出 *mut T 並將其轉換為 &T ,則 T 中的數據必須保持不可變(以 T 中找到的任何 UnsafeCell 數據為模,當然)直到該引用的生命周期到期。同樣,如果您創建釋放到安全代碼的 &mut T 引用,則在該引用過期之前您不得訪問 UnsafeCell 中的數據。

  • 在任何時候,您都必須避免數據競爭。如果多個線程可以訪問相同的 UnsafeCell ,則任何寫入都必須與所有其他訪問(或使用原子)具有正確的 happens-before 關係。

為了幫助進行正確的設計,以下場景被明確聲明為對單線程代碼合法:

  1. &T 引用可以發布到安全代碼,並且可以將 co-exist 與其他 &T 引用一起發布,但不能與 &mut T 一起使用

  2. 如果其他 &mut T&T co-exist 都沒有,則 &mut T 引用可以發布到安全代碼。 &mut T 必須始終是唯一的。

請注意,在改變&UnsafeCell<T>(即使其他&UnsafeCell<T>引用別名單元格)是可以的(假設你以其他方式強製執行上述不變量),它仍然是未定義的行為有多個&mut UnsafeCell<T>別名。那是,UnsafeCell是一個包裝器,旨在與共享訪問(IE。,通過一個&UnsafeCell<_>參考);處理時沒有任何魔法獨家的訪問(例如,通過一個&mut UnsafeCell<_>):在此期間,單元格和包裝值都不能使用別名&mut借。這是由core::cell::UnsafeCell.get_mut訪問器,它是一個安全的產生一個吸氣劑&mut T.

例子

下麵是一個示例,展示了如何對 UnsafeCell<_> 的內容進行完全變異,盡管有多個引用對單元格進行別名:

use std::cell::UnsafeCell;

let x: UnsafeCell<i32> = 42.into();
// Get multiple / concurrent / shared references to the same `x`.
let (p1, p2): (&UnsafeCell<i32>, &UnsafeCell<i32>) = (&x, &x);

unsafe {
    // SAFETY: within this scope there are no other references to `x`'s contents,
    // so ours is effectively unique.
    let p1_exclusive: &mut i32 = &mut *p1.get(); // -- borrow --+
    *p1_exclusive += 27; //                                     |
} // <---------- cannot go beyond this point -------------------+

unsafe {
    // SAFETY: within this scope nobody expects to have exclusive access to `x`'s contents,
    // so we can have multiple shared accesses concurrently.
    let p2_shared: &i32 = &*p2.get();
    assert_eq!(*p2_shared, 42 + 27);
    let p1_shared: &i32 = &*p1.get();
    assert_eq!(*p1_shared, *p2_shared);
}

以下示例展示了對 UnsafeCell<T> 的獨占訪問意味著對其 T 的獨占訪問這一事實:

#![forbid(unsafe_code)] // with exclusive accesses,
                        // `UnsafeCell` is a transparent no-op wrapper,
                        // so no need for `unsafe` here.
use std::cell::UnsafeCell;

let mut x: UnsafeCell<i32> = 42.into();

// Get a compile-time-checked unique reference to `x`.
let p_unique: &mut UnsafeCell<i32> = &mut x;
// With an exclusive reference, we can mutate the contents for free.
*p_unique.get_mut() = 0;
// Or, equivalently:
x = UnsafeCell::new(0);

// When we own the value, we can extract the contents for free.
let contents: i32 = x.into_inner();
assert_eq!(contents, 0);

相關用法


注:本文由純淨天空篩選整理自rust-lang.org大神的英文原創作品 Struct core::cell::UnsafeCell。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。