Why can't I upgrade a known-exclusive &T to &mut T?
⚓ Rust 📅 2026-04-16 👤 surdeus 👁️ 4Let's say I've written some code that borrows a shared reference to some data, but I happen to be able to prove that it is the only shared reference to that data. Is it okay to play pointer tricks in order to "upgrade" it to an exclusive reference?
The real use case here is writing an iteration algorithm only once, for shared references, and then implementing iter_mut on top of it. Because iter_mut borrows the container exclusively, I know that the shared references returned by iter are the only ones that currently exist.
Here is a simplified example (playground). Is this UB?
struct S<T>(T);
fn upgrade_ref<'a, T>(s: &'a mut S<T>) {
let t: &T = &s.0; // No problem.
let p: *const T = t; // No problem.
let p: *mut T = p as *mut T; // No problem.
drop(t); // There is no shared reference left (?).
let t_mut: &'a mut T = unsafe { &mut *p };
}
Well okay, now that I write that I see that the compiler has some data flow analysis that is smart enough to tell me off somehow:
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> src/lib.rs:10:37
|
8 | let p: *mut T = p as *mut T; // No problem.
| ----------- casting happened here
9 | drop(t); // There is no shared reference left (?).
10 | let t_mut: &'a mut T = unsafe { &mut *p };
| ^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
= note: `#[deny(invalid_reference_casting)]` on by default
So I guess it's not supposed to be allowed. But then my question is: why? What reasonable optimization is impeded by the assumptions in this function?
And then, assuming it's UB for a good reason, what is the usual pattern for not needing to write an iterator twice for each container? I guess I can write an iterator that produces raw pointers and map those to references of each type; is there a better way?
14 posts - 7 participants
🏷️ Rust_feed