Experiment: Borrowing only one field in a struct trough an associated method

After someone mentioned it (cannot remember who), I was trying to find a way to only borrow few fields of a struct, instead of borrowing the whole struct.

struct Foo {
    a: i32,
    b: String,
}

impl Foo {
    fn a_as_ref(&self) -> &i32 {
        &self.a
    }
    fn b_as_ref_mut(&mut self) -> &mut String {
        &mut self.b
    }
    fn do_a_thing(mut self) {
        // This wont compile:
        // error[E0502]: cannot borrow `self` as mutable because it is also borrowed as immutable

        let a = self.a_as_ref();
        //         ---- immutable borrow occurs here
        let b = self.b_as_ref_mut();  
        //          ^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
        
        fn use_a_and_b(_: &i32, _: &mut String) { /* code */ }

        use_a_and_b(a, b);
        //          - immutable borrow later used here
    }
    fn do_a_thing_fixed(mut self) {        
        // Sometimes this can avoided by cloning or accessing the fields directly,
        // but depending on the use case this may not be possible
        
        fn use_a_and_b(_: &i32, _: &mut String) { /* code */ }

        use_a_and_b(&self.a, &mut self.b)
    }
}

So I came up with this code which is possibly sound (?) (at least MIRI doesn't complain)

Code
#![feature(arbitrary_self_types)]

use core::ptr::NonNull;

use core::ptr::addr_of;
use core::ptr::addr_of_mut;

use core::ops::Deref;
use core::ops::DerefMut;

use core::marker::PhantomData;

#[derive(Debug, Default)]
struct Vecs<T> {
    from: Vec<T>,
    into: Vec<T>,
}

struct MayAlias<'a, T>(
    ///
    /// # Invariant
    ///
    /// This pointer is always valid to dereference
    ///
    NonNull<T>,
    PhantomData<&'a mut T>,
);

impl<'a, T> MayAlias<'a, T> {
    fn new(ref_mut: &'a mut T) -> Self {
        Self(NonNull::from(ref_mut), PhantomData)
    }

    unsafe fn as_ref<U>(&self, f: impl FnOnce(&Self) -> *const U) -> &'a U {
        &*f(self)
    }

    unsafe fn as_ref_mut<U>(&mut self, f: impl FnOnce(&mut Self) -> *mut U) -> &'a mut U {
        &mut *f(self)
    }
}

impl<'a, T> Deref for MayAlias<'a, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe { self.as_ref(|this| this.0.as_ptr()) }
    }
}

impl<'a, T> DerefMut for MayAlias<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { self.as_ref_mut(|this| this.0.as_ptr()) }
    }
}

impl<T> Vecs<T> {
    fn push(&mut self, item: T) {
        self.from.push(item)
    }
    
    fn _iter(from: &Vec<T>) -> impl Iterator<Item = &T> {
        from.iter()
    }
    fn _extend(into: &mut Vec<T>, iter: impl IntoIterator<Item = T>) {
        into.extend(iter)
    }

    ///
    /// # Invariant
    ///
    /// This function borrows `self.from`
    ///
    /// # Safety
    ///
    /// `self.from` may not be mutably borrowed (`self` may not be mutably borrowed either)
    ///
    unsafe fn iter<'a>(self: &MayAlias<'a, Self>) -> impl Iterator<Item = &'a T> {
        Self::_iter(self.as_ref(|this| addr_of!(this.from)))
    }
    
    ///
    /// # Invariant 
    ///
    /// This funcion borrows `self.into` as mutable
    ///
    /// # Safety
    ///
    /// `self.into` may not be borrowed (`self` may not be borrowed either)
    ///
    unsafe fn extend<'a>(self: &mut MayAlias<'a, Self>, iter: impl IntoIterator<Item = T>) {
        Self::_extend(self.as_ref_mut(|this| addr_of_mut!(this.into)), iter)
    }
}

fn main() {
    let mut vr = Vecs::default();

    vr.push(0);
    vr.push(1);
    vr.push(2);
    vr.push(3);

    let mut may_alias = MayAlias::new(&mut vr);

    unsafe {
        //
        // This is possibly safe because:
        //
        // * `Self::iter` only borrows `self.from`
        // * `Self::extend` only borrows `self.into`
        //
        may_alias.extend(
            may_alias
                .iter()
                .filter(|a| *a % 3 == 0)
                .copied()
                .flat_map(|n| [n + 1, n + 4]),
        )
    };

    println!("{:#?}", vr)
}

you could do that in Rust already:

struct Foo {
    a: i32,
    b: String,
}

impl Foo {
    fn a(Self{a, ..}: &Self) -> &i32 {
        a
    }
}

This still borrows the whole struct (Playground).

Function argument patterns don't have effect on function signatures. (Self::a has the type fn(&Self) -> &i32)

Rust doesn't like getters and setters. There were some discussions about that, but the best solution is just to avoid them.

There are various solutions: split them into two structs (it can be a sign of incorrect design, too - should a and b be really together?), return both references from a single method (that is, something like fn a_and_b(&mut self) -> (&i32, &mut String) { (&self.a, &self.b) }, and in the hardest cases, if you really can't, you can use unsafe code.

Your code seems sound, but if you must, why not just take a raw pointer?

2 Likes
        use_a_and_b(&self.a, &mut self.b)

Yes, this is fine. When you borrow fields within the same method the borrow checker can see how they're used, and that it's safe.

When getters/setters use &mut self they borrow all fields of self. This is by design to hide implementation details of methods.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.