Reputation: 396
I am trying to store and use an optional callback handle in Rust which works like a method to the structure I am storing it in. It works as long as I do not pass a reference to itself to the callback. But doing so gives me a lifetime error for the used object references (E0312). The lifetime seems to be the same and I cannot figure out what to change to get this working.
type Callback<'a> = Fn(&'a mut Func, i32) -> i32;
struct Func<'a> {
val: i32,
func: Option<Box<Callback<'a>>>,
}
impl<'a, 'b> Func<'b> {
fn exec(&'a mut self, val: i32) -> i32 {
if let Some(ref f) = self.func {
return f(self, val);
};
0i32
}
}
fn main() {
let mut a32 = Func{
val: 10i32,
func: Some(Box::new(|ref mut s, val: i32| -> i32 {
let v = s.val;
s.val += 1;
val * 32 + v
}))
};
println!("a32(4) = {}", a32.exec(4i32));
println!("a32(4) = {}", a32.exec(4i32));
}
Is there a way to fix this or did I come across a compiler bug?
Using rustc 1.15.0 (10893a9a3 2017-01-19).
See also on Rust playground.
I also tried the same without explicit lifetimes but then I run into the problem that I cannot alias references in Rust (E0502).
I know that Rust tries to prevent this to avoid data races but would this mean that I always need to create a copy of my object in these cases?
The following does not work either giving me an error, that borrowed content cannot be moved out (E0507).
impl Func {
fn exec(&mut self, val: i32) -> i32 {
if self.func.is_some() {
return self.func.unwrap()(self, val);
};
0i32
}
}
But I could not find a way to clone the boxed function...
Upvotes: 3
Views: 275
Reputation: 300099
You have a borrow issue here:
self.func
immutablyself
mutably at the same timeThis is not allowed, because it could allow you to change func
while using it, which heralds troubles.
You could attempt to change Callback
to only pass in &mut i32
instead, but then you would hit lifetime unification issues:
exec
takes &'a mut self
, then you anchor the object, borrowing it for the rest of its lifetime,'a
, and you required 'a
in the signature of Callback
.Neither situation works.
The solution, thus, is to avoid the lifetime in the first place.
It's also easier (on borrowing) NOT to pass an instance of self
but just to pass a reference to self.val
so I present that first:
type Callback = Fn(&mut i32, i32) -> i32;
struct Func {
val: i32,
func: Option<Box<Callback>>,
}
impl Func {
fn exec(&mut self, val: i32) -> i32 {
if let Some(ref f) = self.func {
return f(&mut self.val, val);
};
0i32
}
}
fn main() {
let mut a32 = Func{
val: 10i32,
func: Some(Box::new(|s: &mut i32, val: i32| -> i32 {
let v = *s;
*s += 1;
val * 32 + v
}))
};
println!("a32(4) = {}", a32.exec(4i32));
println!("a32(4) = {}", a32.exec(4i32));
}
If you want to really pass Func
, you need to "option dance":
impl Func {
fn exec(&mut self, val: i32) -> i32 {
let func = self.func.take();
let res = if let Some(ref f) = func {
f(self, val)
} else {
0i32
};
self.func = func;
res
}
}
And be aware that self.func
is empty in the callback.
Upvotes: 3