#别名 #原始类型 #不安全 #可变性 #无std

已删除 不安全别名单元

原始类型用于别名可变性

0.0.1 2022年4月22日
0.0.0 2022年4月21日

#14 in #别名

MIT/Apache

7KB

Rust中的别名可变性原始类型

在Rust中,&mut T通常不允许引用别名内存。但当编写自引用结构体时,需要别名可变引用。此crate提供了UnsafeAliasCell<T>原始类型。它的工作方式与UnsafeCell<T>类似,后者来自标准库

UnsafeCell<T>放弃了&T的不变性保证:共享引用&UnsafeCell<T>可能指向正在被修改的数据。

UnsafeAliasCell<T>放弃了&mut T的唯一性保证:
一个唯一的可变引用&mut UnsafeAliasCell<T>可能指向正在被修改的数据。

使用UnsafeAliasCell<T>

在使用UnsafeAliasCell<T>时需要小心,因为错误的使用会导致未定义行为。

即使在使用 UnsafeAliasCell<T> 的情况下,创建多个别名 &mut T 被视为未定义行为。但你可以创建多个别名 *mut T/*const T

示例

在打算创建别名的部分使用 UnsafeAliasCell<T>

# use unsafe_alias_cell::UnsafeAliasCell;
pub struct SelfReferential {
	item: UnsafeAliasCell<i32>,
	ptr: *const i32,
}

现在你可以对 item 调用 .get() 并将指针存储在 ptr 中。只要 SelfReferential 保持 固定,你就可以使用 ptr 来读取项。

未定义行为

对于包含 UnsafeAliasCell<T> 的任何类型实现 Unpin 是未定义行为。

.get() 返回的指针转换为

  • &mut T,当存在另一个指向该单元格内部的指针(&T*const T*mut T)时,是未定义行为。
  • &T,当存在另一个指向该单元格内部的可变指针(*mut T)时,是未定义行为。

类似于 UnsafeCell<T>,你需要确保为任何你创建的引用(来自 标准库)保证别名规则

  • 如果您使用生命周期为 'a(无论是 &T 还是 &mut T 引用)创建一个安全引用,并且该引用可以通过安全代码访问(例如,因为您已返回它),那么您不能以任何与该引用相矛盾的方式访问数据,直到 'a 的生命周期结束。例如,这意味着如果您从一个 UnsafeAliasCell<T> 中取出 *mut T 并将其转换为 &T,那么 T 中的数据必须保持不变(当然,包括 T 内部找到的任何 UnsafeCell<U>/UnsafeAliasCell<U> 数据)。同样,如果您创建一个 &mut T 引用并将其释放给安全代码,那么在该引用过期之前,您不能访问 UnsafeAliasCell<T> 中的数据。
  • 始终要避免数据竞争。如果有多个线程可以访问相同的 UnsafeAliasCell<T>,那么任何写入都必须与其他所有访问有正确的发生先于关系(或使用原子操作)。

它是如何工作的?

根据当前规则,所有 !Unpin 类型都不会为 &T&mut T 生成 noalias,因此在 LLVM 中能够产生别名。为了使 UnsafeAliasCell<T> 保持有效,它因此必须仅包含 !Unpin 类型。

无运行时依赖