Search code examples
referencerustimmutability

How to avoid writing duplicate accessor functions for mutable and immutable references in Rust?


A few times, I've run into the scenario where an accessor method is needed for both mutable and immutable references.

For ~3 lines it isn't a problem to duplicate the logic, but when the logic gets more complex, it's not nice to copy-paste large blocks of code.

I'd like to be able to re-use the code for both.

Does Rust provide some way handle this better then copy-pasting code, or using unsafe casts?

e.g.:

impl MyStruct {
    pub fn get_foo(&self) -> &Bar {
        // ~20 lines of code
        // --- snip ---
        return bar;
    }
    pub fn get_foo_mut(&mut self) -> &mut Bar {
        // ~20 lines of code
        // (exactly matching previous code except `bar` is mutable)
        // --- snip ---
        return bar;
    }
}

Here is a more detailed excerpt of a code-base where an immutable return argument was cast to mutable to support both immutable and mutable versions of a function. This uses a wrapped pointer type (ConstP and MutP for immutable and mutable references), but the logic of the function should be clear.

pub fn face_vert_share_loop<V, F>(f: F, v: V) -> LoopConstP
    where V: Into<VertConstP>,
          F: Into<FaceConstP>
{
    into_expand!(f, v);

    let l_first = f.l_first.as_const();
    let mut l_iter = l_first;
    loop {
        if l_iter.v == v {
            return l_iter;
        }

        l_iter = l_iter.next.as_const();
        if l_iter == l_first {
            break;
        }
    }

    return null_const();
}
pub fn face_vert_share_loop_mut(f: FaceMutP, v: VertMutP) -> LoopMutP {
    let l = face_vert_share_loop(f, v);
    return unsafe {
        // Evil! but what are the alternatives?
        // Perform an unsafe `const` to `mut` cast :(
        // While in general this should be avoided,
        // its 'OK' in this case since input is also mutable.
        l.as_mut()
    };
}

Solution

  • You don't, really. Recall that T, &T and &mut T are all different types. In that context, your question is the same as asking "How to avoid writing duplicate accessor functions for String and HashMap".

    Matthieu M had the right terms "abstract over the mutability":

    The TL;DR is that Rust would likely need to be enhanced with new features to support this. Since no one has succeeded, no one is 100% sure which features those would need to be. The current best guess is higher kinded types (HKT).