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?