本文簡要介紹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>
.
UnsafeCell
API 本身在技術上非常簡單: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 關係。
為了幫助進行正確的設計,以下場景被明確聲明為對單線程代碼合法:
-
&T
引用可以發布到安全代碼,並且可以將 co-exist 與其他&T
引用一起發布,但不能與&mut T
一起使用 -
如果其他
&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 UnsafeCell.new用法及代碼示例
- Rust UnsafeCell.into_inner用法及代碼示例
- Rust UnsafeCell.get用法及代碼示例
- Rust UnsafeCell.get_mut用法及代碼示例
- Rust UnsafeCell.raw_get用法及代碼示例
- Rust UnixStream用法及代碼示例
- Rust UnixStream.take_error用法及代碼示例
- Rust UnixListener.accept用法及代碼示例
- Rust UnixListener.bind用法及代碼示例
- Rust UnixDatagram.peek_from用法及代碼示例
- Rust UnixDatagram.recv_from用法及代碼示例
- Rust UnixListener.incoming用法及代碼示例
- Rust UnixStream.read_timeout用法及代碼示例
- Rust UnixDatagram.take_error用法及代碼示例
- Rust Union用法及代碼示例
- Rust UnixDatagram.peek用法及代碼示例
- Rust UnixDatagram.send_to_addr用法及代碼示例
- Rust UnixDatagram.read_timeout用法及代碼示例
- Rust UnixDatagram.set_nonblocking用法及代碼示例
- Rust UnixDatagram.connect用法及代碼示例
- Rust UnixDatagram.set_read_timeout用法及代碼示例
- Rust UnixStream.peer_addr用法及代碼示例
- Rust UnixDatagram.send_to用法及代碼示例
- Rust UnixStream.send_vectored_with_ancillary用法及代碼示例
- Rust UnixStream.peek用法及代碼示例
注:本文由純淨天空篩選整理自rust-lang.org大神的英文原創作品 Struct core::cell::UnsafeCell。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。