&mut T
and &mut T
results in a compilation error; this is great, it's objectively wrong to borrow mutably twice.
Is *mut T
and*mut T
undefined behaviour or is this a perfectly valid thing to do? That is, is mutable pointer aliasing valid?
What makes it even worse is that &mut T
and *mut T
actually compiles and works as intended, I can modify a value through the reference, the pointer, and then the reference again... but I've seen someone say that it's undefined behaviour. Yeah, "someone said so" is the only information I have.
Here's what I tested:
fn main() {
let mut value: u8 = 42;
let r: &mut u8 = &mut value;
let p: *mut u8 = r as *mut _;
*r += 1;
unsafe { *p += 1; }
*r -= 1;
unsafe { *p -= 1; }
println!("{}", value);
}
and of course, the main point of question:
Note — Thanks to trentcl for pointing out this example actually causes a copy when creating p2
. This can be confirmed by replacing u8
with a non-Copy
type. The compiler then complains about a move. Sadly, this does not get me closer to the answer, only reminds me that I can get unintended behaviour without it being undefined behaviour, simply because of Rust's move semantics.
fn main() {
let mut value: u8 = 42;
let p1: *mut u8 = &mut value as *mut _;
// this part was edited, left in so it's easy to spot
// it's not important how I got this value, what's important is that it points to same variable and allows mutating it
// I did it this way, hoping that trying to access real value then grab new pointer again, would break something, if it was UB to do this
//let p2: *mut u8 = &mut unsafe { *p1 } as *mut _;
let p2: *mut u8 = p1;
unsafe {
*p1 += 1;
*p2 += 1;
*p1 -= 1;
*p2 -= 1;
}
println!("{}", value);
}
Both yield:
42
Does this imply that two mutable pointers pointing to the same location and being dereferenced at different times is not undefined behaviour?
I don't think testing this on compiler is a good idea to begin with, as undefined behaviour could have anything happen, even printing 42
as if nothing is wrong. I mention it anyway as this is one of things I tried, hoping to get an objective answer.
I have no clue how to write a test that could force erratic behaviour that would make it dead obvious that this doesn't work because it's not used as intended, if that's even possible to do so.
I'm aware that this is very likely to be undefined behaviour and break in a multithreaded environment no matter what. I would expect a more detailed answer than that, though, especially if mutable pointer aliasing IS NOT undefined behaviour. (This would in fact be awesome, because while I use Rust for reasons like everyone else - memory safety, to say the least... I expect to still retain a shotgun that I could point anywhere, without it being locked onto my feet. I can have aliased "mutable pointers" without blowing my feet off in C.)
This is a question about whether I can, not about whether I should. I want to dive head-on into unsafe Rust, just to learn about it, but it feels like there's not enough information unlike in "horrible" languages like C about what's undefined behaviour and what's not.