created_at2022-04-21 22:08:19.698672
updated_at2022-04-22 09:00:50.521917
descriptionPrimitive for aliasing mutability



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.


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 an [UnsafeAliasCell<T>] and cast it to an &T, then the data in T must remain immutable (modulo any [UnsafeCell<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 the [UnsafeAliasCell<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.

Commit count: 11

cargo fmt