0.0.1 |
|
---|---|
0.0.0 |
|
#14 in #别名
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
类型。