The AliasGuardMut definition is
pub struct AliasingGuardMut<'a, T: ?Sized> {
ptr: NonNull<T>,
_marker: PhantomData<&'a mut T>,
}
Aka it is alias to the real raw pointer that is used in the original code. Changing all of them to references is equivalent to changing the raw pointers in the original code to references. So it defeats the purpose of simulating the mixing of raw pointers and references
Eg:
struct Arena<T> {
ptr: *mut T,
}
Becomes:
struct Arena<'a, T> {
guard: &'a mut T,
}
struct Buffer {
ptr: NonNull<Vec<i32>>,
}
Becomes:
struct Buffer<'a> {
guard: &'a mut Vec<i32>,
}
struct SlotMap<T> {
slots: Vec<NonNull<T>>,
}
Becomes:
struct SlotMap<'a, T> {
slot: Option<&'a mut T>,
}
The examples are intentionally made with human mistakes included, to demonstrate that the guard shows compile time error where the raw direct raw pointers and references interactions are human error prone
The body of the example is not important, it is just a simulation of raw pointers and references interaction which is definetely exist in real world
Please elaborate more maybe with Miri to show the UB. Because the method .mutable_reference() gives mutable reference from the raw pointer, not the original data, so it does not invalidate previous pointers permanently, only when the mutable reference is active, that the guard will prevent a use of pointer while said mutable reference is still active via the method .with_mutable_pointer(). After said mutable reference is inactive, the pointer is valid again
The full code with intentional human mistake is like this, to demonstrate that it is able to show compile time error
use std::marker::PhantomData;
use std::ptr::NonNull;
pub struct AliasingGuardMut<'a, T: ?Sized> {
ptr: NonNull<T>,
// SAFETY:
// This models exclusive mutable ownership over `T` for lifetime `'a`.
//
// The guard conceptually behaves like it owns an `&'a mut T`,
// which prevents aliasing mutable borrows through Rust's borrow checker.
//
// `PhantomData<&'a mut T>` is important because:
// - it enforces invariance over `T`
// - it tells the compiler this type semantically contains `&mut T`
// - it enables borrow checking rules for aliasing/exclusivity
// - it prevents multiple mutable guards existing simultaneously in safe code
_marker: PhantomData<&'a mut T>,
}
impl<'a, T: ?Sized> AliasingGuardMut<'a, T> {
#[inline(always)]
pub fn from_reference(value: &'a mut T) -> Self {
Self {
// SAFETY:
// `NonNull::from` is safe because `&mut T` is guaranteed:
// - non-null
// - properly aligned
// - valid for reads/writes for `'a`
ptr: NonNull::from(value),
_marker: PhantomData,
}
}
#[inline(always)]
pub fn immutable_reference(&self) -> &T {
// SAFETY:
// The original `&mut T` guarantees:
// - pointer validity
// - proper alignment
// - initialized memory
//
// Returning `&T` from `&self` is safe because:
// - immutable references may alias other immutable references
// - Rust reference rules prevent obtaining `&mut self` simultaneously with this reference in safe code
unsafe { self.ptr.as_ref() }
}
#[inline(always)]
pub fn mutable_reference(&mut self) -> &mut T {
// SAFETY:
// `&mut self` guarantees exclusive access to the guard.
//
// Because the guard semantically owns an exclusive `&mut T`,
// this ensures no competing mutable references can exist
// through this API in safe Rust.
//
// WARNING:
// Raw pointers previously extracted from this guard may still
// exist and can violate aliasing rules if used incorrectly.
// Safe Rust callers cannot trigger UB here, but unsafe callers can.
unsafe { self.ptr.as_mut() }
}
#[inline(always)]
pub fn with_immutable_reference<R>(&self, f: impl FnOnce(&T) -> R) -> R {
// SAFETY:
// Same reasoning as `immutable_reference`.
//
// The reference is scoped to the closure call,
// preventing it from escaping accidentally.
unsafe { f(self.ptr.as_ref()) }
}
#[inline(always)]
pub fn with_mutable_reference<R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R {
// SAFETY:
// Same reasoning as `mutable_reference`.
//
// The mutable reference is scoped to the closure execution,
// which helps reduce accidental misuse duration.
unsafe { f(self.ptr.as_mut()) }
}
#[inline(always)]
pub fn with_immutable_pointer<R>(&self, f: impl FnOnce(*const T) -> R) -> R {
// SAFETY:
// - Rust reference rules prevent obtaining `&mut self` simultaneously with this reference in safe code
//
// In particular:
// - The immutable raw pointer is scoped to the closure execution
// which makes able to create `&mut` without invalidating the pointers
// - It prevents calling immutable raw pointer while `&mut` is still active because it violates the aliasing rules
f(self.ptr.as_ptr())
}
#[inline(always)]
pub fn with_mutable_pointer<R>(&mut self, f: impl FnOnce(*mut T) -> R) -> R {
// SAFETY:
// - Rust reference rules prevent obtaining `&mut self` simultaneously with this reference in safe code
//
// In particular:
// - The mutable raw pointer is scoped to the closure execution
// which makes able to create `&` or `&mut` without invalidating the pointers
// - It prevents calling mutable raw pointer while `&` or `&mut` is still active because it violates the aliasing rules
f(self.ptr.as_ptr())
}
#[inline(always)]
pub unsafe fn as_ptr(&mut self) -> *mut T {
// SAFETY:
// This exists to make if closure based pointer is not enough, then this unsafe method can be used
// Returning raw pointers is safe by itself.
//
// However, once the pointer escapes, this type can no longer
// enforce aliasing guarantees.
//
// The caller must ensure:
// - no invalid reference/raw-pointer combinations are used
// - no aliasing UB occurs
// - do not write to the pointer while `&` or `&mut` to same memory is still active
// - do not read the pointer while `&mut` to same memory is still active
// - be aware that `&mut` creation that points to same address of this pointer will invalidate this pointer
// - pointer is not used after underlying value becomes invalid
self.ptr.as_ptr()
}
#[inline(always)]
pub fn clone_guard(&self) -> Self {
Self {
ptr: self.ptr,
_marker: PhantomData,
}
}
#[inline(always)]
pub fn close(self) {
// SAFETY:
// Consuming `self` ends the guard lifetime early.
//
// This can be useful to release the conceptual mutable borrow
// before the surrounding scope ends.
}
}
struct Node<'a> {
next: Option<AliasingGuardMut<'a, Node<'a>>>,
value: i32,
}
struct List<'a> {
head: Option<AliasingGuardMut<'a, Node<'a>>>,
guard: Option<AliasingGuardMut<'a, Node<'a>>>,
}
impl<'a> List<'a> {
fn new() -> Self {
Self {
head: None,
guard: None,
}
}
fn push_front(&mut self, node: &'a mut Node<'a>) {
node.next = self.head.take();
self.head = Some(
AliasingGuardMut::from_reference(node)
);
self.guard = Some(
AliasingGuardMut::from_reference(
node
)
);
}
fn first_mut(&mut self) -> Option<&mut Node<'a>> {
self.guard.as_mut().map(|guard| {
guard.mutable_reference()
})
}
}
fn increment(node: &mut Node<'_>) {
node.value += 1;
}
fn main() {
let mut node = Box::new(Node {
next: None,
value: 10,
});
let mut list = List::new();
list.push_front(&mut *node);
let a = list.first_mut().unwrap();
increment(a);
let b = list.first_mut().unwrap();
b.value += 10;
println!("{}", a.value);
}
To find the newest AliasGuard code, it will be updated in bottom section of the opening post everytime there is change