4

I have a struct and method like this:

pub struct S {
    a: Vec<u32>,
    b: Vec<u32>,
}

impl S {
    // Pretend that this function has a similar signature but an
    // implementation that is much more complicated and verbose
    fn b_index_mut(&mut self, i: usize) -> &mut u32 {
        &mut self.b[i]
    }

    pub fn foo(&mut self) {
        for (i, x) in self.a.iter_mut().enumerate() {
            *self.b_index_mut(i) += *x;
        }
    }
}

S.foo() will not compile:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src\main.rs:15:14
   |
14 |         for (i, x) in self.a.iter_mut().enumerate() {
   |                       ------ first mutable borrow occurs here
15 |             *self.b_index_mut(i) += *x;
   |              ^^^^ second mutable borrow occurs here
16 |         }
   |         - first borrow ends here

There is this possible implementation which eliminates this error by simply moving the body of S.b_index_mut() into S.foo():

impl S {
    pub fn foo(&mut self) {
        for (i, x) in self.a.iter_mut().enumerate() {
            self.b[i] += *x;
        }
    }
}

However, as I said in the comment in the first implementation, the real S.b_index_mut() that I have in mind is much more verbose that the example, and it is really something that should have its own function.

A possible work around is to pass b as an argument to S.b_index_mut():

impl S {
    // Pretend that this function has a similar signature but an
    // implementation that is much more complicated and verbose
    fn b_index_mut(b: &mut Vec<u32>, i: usize) -> &mut u32 {
        &mut b[i]
    }

    pub fn foo(&mut self) {
        for (i, x) in self.a.iter_mut().enumerate() {
            *S::b_index_mut(&mut self.b, i) += *x;
        }
    }
}

This solution compiles and solves the abstraction problem. However, it seems inelegant. Something feels wrong to me about calling a struct function that doesn't take self for anything other than a constructor.

If the real version of S.b_index_mut() requires factoring in more members of S (that are not a), just writing the function call itself can become quite verbose.

Is there an elegant solution to this problem?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Kai Schmidt
  • 701
  • 8
  • 14
  • 4
    [Move all of the relevant variables to a new struct, move the method to that struct, then embed the new struct in your existing one](https://play.rust-lang.org/?gist=059fa974e0d1570d12b65b875d2b6ec5&version=stable). That's your "real" abstraction, anyway. – Shepmaster Jan 23 '18 at 19:01

0 Answers0