Reducing Raw Pointer Footguns: Preventing Reference Aliasing Violations at Compile Time
Rust Internals [Unofficial]
May 16, 2026
Here's how all your AliasingGuardMut can be written with simply a &mut reference without raw pointers! So while AliasingGuardMut may be safer than using raw pointers, it doesn't seem to enable anything that &mut references can do, So where is the actual benefit of using AliasingGuardMut? Note that in your examples there were even examples of unsoundness and reference aliasing violation where I left a comment on, so AliasingGuardMut seem to not even prevent them.
fn a(b: &mut Vec<i32>) {
*b = vec![1];
}
// some other code that must take pointer
// eg FFI code, low level code that takes pointer
// or 3rd party library code that takes pointer
fn b(b: *mut Vec<i32>) {
unsafe { *b = vec![1] }
}
fn main() {
let mut s = vec![1, 2, 3];
let reff = &mut s;
a(reff);
b(reff);
}
guard: &'a mut i32
}
impl<'a> Holder<'a> {
fn write(&mut self, val: i32) {
*self.guard = val;
}
}
fn main() {
let mut x = 5;
let mut h = Holder {
guard: &mut x,
};
let r = &mut *h.guard;
h.write(10);
*r += 1;
}
struct Arena<'a, T> {
guard: &'a mut T,
}
impl<'a, T> Arena<'a, T> {
fn get_mut(&self) -> &mut T {
// This is a compile time error.
// I hope the existing self.guard.mutable_reference() was a compile time error too
// or it would have been unsound (worse than a raw pointer because it didn't even require unsafe!)
&mut *self.guard
}
}
fn main() {
let mut value = String::from("hello");
let mut arena = Arena {
guard: &mut value,
};
let a = arena.get_mut();
let b = arena.get_mut();
a.push_str(" world");
b.push_str(" !!!");
}
use std::ptr::NonNull;
struct Node {
next: Option<NonNull<Node>>,
value: i32,
}
struct List<'a> {
head: Option<NonNull<Node>>,
guard: Option<&'a mut Node>,
}
impl<'a> List<'a> {
fn new() -> Self {
Self {
head: None,
guard: None,
}
}
unsafe fn push_front(&mut self, node: &'a mut Node) {
node.next = self.head;
self.head = Some(NonNull::from(&mut *node));
// Note that both the new and the old code invalidate the pointer in `self.head`
// I'm not sure how you intended to use it, but it shows that AliasingGuardMut also has footguns
// self.guard = Some(AliasingGuardMut::from_reference(node));
self.guard = Some(node);
}
fn first_mut(&mut self) -> Option<&mut Node> {
self.guard.as_mut()
}
}
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();
unsafe {
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);
}
struct Buffer<'a> {
guard: &'a mut Vec<i32>,
}
impl<'a> Buffer<'a> {
fn new(vec: &'a mut Vec<i32>) -> Self {
Self {
guard: vec,
}
}
fn get_mut(&mut self) -> &mut Vec<i32> {
&mut *self.guard
}
}
fn append_data(vec: &mut Vec<i32>) {
vec.push(100);
}
fn main() {
let mut data = vec![1, 2, 3];
let mut buffer = Buffer::new(&mut data);
let a = buffer.get_mut();
append_data(a);
let b = buffer.get_mut();
b.push(200);
a.push(300);
println!("{:?}", a);
}
struct SlotMap<'a, T> {
slot: Option<&'a mut T>,
}
impl<'a, T> SlotMap<'a, T> {
fn new() -> Self {
Self {
slot: None,
}
}
fn insert(&mut self, value: &'a mut T) {
self.slot = Some(value);
}
fn get_mut(&mut self, _: usize) -> &mut T {
self.slot
.as_mut()
.unwrap()
}
}
fn update_user(user: &mut String) {
user.push_str(" updated");
}
fn main() {
let mut user = String::from("alice");
let mut map = SlotMap::new();
map.insert(&mut user);
let current = map.get_mut(0);
update_user(current);
let another = map.get_mut(0);
another.push_str(" !!!");
println!("{}", current);
}
Discussion in the ATmosphere