当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


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。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。