Independent borrows of fields behind a Deref

⚓ Rust    📅 2025-09-10    👤 surdeus    👁️ 10      

surdeus

Warning

This post was published 63 days ago. The information described in this article may have changed.

You 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

Read full topic

🏷️ Rust_feed