What's the fastest idiomatic way to mutate multiple struct fields at the same time?

648 Views Asked by At

Many libraries allow you to define a type which implements a given trait to be used as a callback handler. This requires you to lump all of the data you'll need to handle the event together in a single data type, which complicates borrows.

For instance, mio allows you to implement Handler and provide your struct when you run the EventLoop. Consider an example with these trivialized data types:

struct A {
  pub b: Option<B>
};

struct B;

struct MyHandlerType {
  pub map: BTreeMap<Token, A>,
  pub pool: Pool<B>
}

Your handler has a map from Token to items of type A. Each item of type A may or may not already have an associated value of type B. In the handler, you want to look up the A value for a given Token and, if it doesn't already have a B value, get one out of the handler's Pool<B>.

impl Handler for MyHandlerType {
    fn ready(&mut self, event_loop: &mut EventLoop<MyHandlerType>, 
             token: Token, events: EventSet) {
       let a : &mut A = self.map.get_mut(token).unwrap();
       let b : B = a.b.take().or_else(|| self.pool.new()).unwrap();
       // Continue working with `a` and `b`
       // ...
    }
}

In this arrangement, even though it's intuitively possible to see that self.map and self.pool are distinct entities, the borrow checker complains that self is already borrowed (via self.map) when we go to access self.pool.

One possible approach to this would be to wrap each field in MyHandlerType in Option<>. Then, at the start of the method call, take() those values out of self and restore them at the end of the call:

struct MyHandlerType {
  // Wrap these fields in `Option`
  pub map: Option<BTreeMap<Token, A>>,
  pub pool: Option<Pool<B>>
}
// ...

fn ready(&mut self, event_loop: &mut EventLoop<MyHandlerType>, 
         token: Token, events: EventSet) {
  // Move these values out of `self`
  let map = self.map.take().unwrap();
  let pool = self.pool.take().unwrap();

  let a : &mut A = self.map.get_mut(token).unwrap();
  let b : B = a.b.take().or_else(|| self.pool.new()).unwrap();
  // Continue working with `a` and `b`
  // ...

  // Restore these values to `self`
  self.map = Some(map);
  self.pool = Some(pool);
}

This works but feels a bit kluge-y. It also introduces the overhead of moving values in and out of self for each method call.

What's the best way to do this?

1

There are 1 best solutions below

1
On BEST ANSWER

To get simultaneous mutable references to different parts of the struct, use destructuring. Example here.

struct Pair {
    x: Vec<u32>,
    y: Vec<u32>,
}

impl Pair {
    fn test(&mut self) -> usize {
        let Pair{ ref mut x, ref mut y } = *self;
        // Both references coexist now
        return x.len() + y.len();
    }
}

fn main() {
    let mut nums = Pair {
        x: vec![1, 2, 3],
        y: vec![4, 5, 6, 7],
    };
    println!("{}", nums.test());
}