0.0.1 |
|
---|---|
0.0.0 |
|
#15 in #aliasing
7KB
Primitive for aliasing mutability in Rust
In rust, &mut T
is not normally allowed to refer to aliasing memory. But when writing self
referential structs, one needs aliasing mutable references. This crate provides the
UnsafeAliasCell<T>
primitive type. It works similar to the UnsafeCell<T>
from the
stdlib:
UnsafeCell<T>
opts-out of the immutability guarantee for&T
: a shared reference&UnsafeCell<T>
may point to data that is being mutated.
UnsafeAliasCell<T>
opts-out of the uniqueness guarantee for &mut T
: a unique mutable reference
&mut UnsafeAliasCell<T>
may point to data that is being mutated.
Using UnsafeAliasCell<T>
One needs to be careful, when using UnsafeAliasCell<T>
, because wrong usage leads to undefined
behavior.
Even when using UnsafeAliasCell<T>
it is considered undefined behavior to create multiple
aliasing &mut T
. But you are allowed to create multiple aliasing *mut T
/*const T
.
Example
Use UnsafeAliasCell<T>
on the part that you intend to alias:
# use unsafe_alias_cell::UnsafeAliasCell;
pub struct SelfReferential {
item: UnsafeAliasCell<i32>,
ptr: *const i32,
}
Now you are allowed to call .get()
on item
and store that pointer in ptr
. For as long as
that SelfReferential
stays pinned, you can use
ptr
to read the item.
Undefined behavior
Implementing Unpin
for any type containing a UnsafeAliasCell<T>
is UB.
It is UB to cast the pointer returned by .get()
to
&mut T
, when there exists another pointer (&T
,*const T
or*mut T
) pointing to the inner of the cell.&T
, when there exists another mutable pointer (*mut T
) pointing to the inner of the cell.
Similar to UnsafeCell<T>
you need to ensure the aliasing rules for any reference you create
(taken from the stdlib):
- If you create a safe reference with lifetime
'a
(either a&T
or&mut T
reference) that is accessible by safe code (for example, because you returned it), then you must not access the data in any way that contradicts that reference for the remainder of'a
. For example, this means that if you take the*mut T
from anUnsafeAliasCell<T>
and cast it to an&T
, then the data in T must remain immutable (modulo anyUnsafeCell<U>
/UnsafeAliasCell<U>
data found within T, of course) until that reference’s lifetime expires. Similarly, if you create a&mut T
reference that is released to safe code, then you must not access the data within theUnsafeAliasCell<T>
until that reference expires. - At all times, you must avoid data races. If multiple threads have access to the same
UnsafeAliasCell<T>
, then any writes must have a proper happens-before relation to all other accesses (or use atomics).
How does it work?
Under the current rules, all types that are !Unpin
do not emit noalias
for &T
and &mut T
in LLVM and are thus able to alias. For UnsafeAliasCell<T>
to be sound, it is therefore
required to be contained in only !Unpin
types.