What does &mut aliasing mean for ZSTs?

A ZST can't hold state, and its address is basically meaningless, with everything in a Vec being at the same address, for example. So what does it mean for &mut to alias with ZSTs? Is it meaningful? Detectable?

Or, concretely, the bar function below would clearly be invalid for anything with size>0, but with a ZST it seems indistinguishable from foo, which is clearly safe.

fn foo(x: &mut [()]) -> (&mut (), &mut (), &mut [()]) {
    let (a, x) = x.split_first_mut().unwrap();
    let (b, x) = x.split_first_mut().unwrap();
    assert!(std::ptr::eq(a, b));
    (a, b, x)
}

fn bar(x: &mut [()]) -> (&mut (), &mut (), &mut [()]) {
    let (a, x) = x.split_first_mut().unwrap();
    let b = unsafe{ &mut *(a as *mut _) };
    assert!(std::ptr::eq(a, b));
    (a, b, &mut x[1..])
}

fn main() {
    println!("{:?}", foo(&mut [();4]));
    println!("{:?}", bar(&mut [();4]));
}
2 Likes

How about a ZST that’s a facade over some FFI code that has state invisible to Rust? Or using &mut ZST for thread safety (ie &mut is Sync). Or more generally, using mutability for the purpose of unique/exclusive access rather than direct mutation (so to speak). Just spitballing here.

1 Like

It kind of depends on how you define aliasing. From the perspective of safety and UB, then as your code demonstrates, merely having two mutable references with the same address isn't UB (this might be a wakeup call to some unsafe code authors). In order to invoke UB from aliased pointers, a read of nonzero length must occur.

It sounds like the rules here are uncertain, so I opened a memory model issue to discuss it:

https://github.com/nikomatsakis/rust-memory-model/issues/44