Independent borrows of fields behind a Deref
⚓ Rust 📅 2025-09-10 👤 surdeus 👁️ 10You can't borrow multiple fields at once if they belong to a type that is behind a DerefMut implementation:
pub struct Person {
name: String,
age: i32,
}
pub struct Wrapper<'a>(&'a mut Person);
impl<'a> Deref for Wrapper<'a> {
type Target = Person;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a> DerefMut for Wrapper<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
pub fn test() {
let mut person = Person {
name: "John Smith".to_owned(),
age: 42,
};
let mut wrapper = Wrapper(&mut person);
let name = &mut wrapper.name;
let age = &mut wrapper.age; // Fails!
println!("{name}");
}
Which makes sense, since deref_mut takes a mutable reference to self, and you can't have two of those at the same time. I came up with an idea to allow something of the sort anyway (explanations below):
pub struct Person {
name: String,
age: i32,
}
mod wrapper {
use super::*;
pub struct Name(());
pub struct Age(());
#[repr(C)]
pub struct Wrapper<'a> {
pub name: Name,
pub age: Age,
inner: *mut Person,
_marker: PhantomData<&'a Person>,
}
impl<'a> Wrapper<'a> {
pub fn new(person: &'a mut Person) -> Wrapper<'a> {
Self {
name: Name(()),
age: Age(()),
inner: person as *mut _,
_marker: PhantomData,
}
}
}
macro_rules! get {
($this:ident.$field:ident) => {{
let person_ptr = *($this as *const Self as *const Wrapper as *const *mut Person);
&raw mut (*person_ptr).$field
}};
}
impl Deref for Name {
type Target = String;
fn deref(&self) -> &Self::Target {
unsafe { &*get!(self.name) }
}
}
impl DerefMut for Name {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *get!(self.name) }
}
}
impl Deref for Age {
type Target = i32;
fn deref(&self) -> &Self::Target {
unsafe { &*get!(self.age) }
}
}
impl DerefMut for Age {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *get!(self.age) }
}
}
}
The idea is that thanks to the #[repr(C)] slapped on Wrapper and to Name and Age being ZSTs, pointers to a Wrapper's name or age fields should share the same value as pointers to the Wrapper itself. And since Wrapper is just, well, a wrapper over a pointer to Person, we can access that too.
So, cast the reference to Name to a pointer to Wrapper (can be skipped), and then cast that to a pointer to a pointer to a Person. From there, get a pointer to the relevant field. This is all that is done in the various Derefs implementations here.
As far as I can tell, this is safe, and by my own tests, it does work 100% of the time. The issue is, Miri tells me there is some UB going on when I dereference the pointer to the pointer to a Person. I don't really know why, although I had some doubts about the value of references to ZSTs. AFAIK, the compiler could decide tomorrow to not give me the actual pointer to Wrapper anymore in Name::deref_mut and instead give me a dangling (but well-aligned) pointer, as references to ZSTs are fake and all. But it works in practice, so maybe there is some guarantees I don't know about or that aren't documented?
Anyway, here's my question: is this actually safe or is this UB?
4 posts - 3 participants
🏷️ Rust_feed