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?
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());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With