#aliasing #primitive #unsafe #mutability #no-std

yanked unsafe-alias-cell

Primitive for aliasing mutability

0.0.1 Apr 22, 2022
0.0.0 Apr 21, 2022

#14 in #aliasing

30 downloads per month

MIT/Apache

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 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.

No runtime deps