Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 357
zslayton Avatar asked Sep 04 '15 17:09

zslayton


1 Answers

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());
}
like image 183
Will Fischer Avatar answered Sep 20 '22 01:09

Will Fischer