Problem
In short: I'm trying to implement an struct that contains a callback to an closure, which captures state mutably. Idea is that user provides callback (closure) and can be informed later when a specific event happens.
I have a working proof-of-concept version (code sample #1).
This PoC works (code sample #1):
fn fun<F>(f: F) -> F
where
F: FnMut() -> (),
{
f
}
fn main() {
let mut abc = "abc".to_string();
let mut f = fun(|| {
abc.push_str(".");
println!("closure: {:?}", abc);
});
f();
f();
f();
println!("end: {:?}", abc);
}
Output:
closure: "abc."
closure: "abc.."
closure: "abc..."
end: "abc..."
This fails (code sample #2):
Same idea (slightly different) as previous but trying to contain closure inside Foo.
struct Foo<'a, T> {
pub cb: Box<dyn FnMut(&T) + 'a>,
}
impl<'a, T> Foo<'a, T> {
fn new(f: impl FnMut(&T) + 'a) -> Self {
Self { cb: Box::new(f) }
}
fn on_change(&mut self, f: impl FnMut(&T) + 'a)
{
self.cb = Box::new(f);
}
}
impl<'a, T> Default for Foo<'a, T>
{
fn default() -> Self {
Self::new(|_| {})
}
}
fn main() {
let mut abc = "abc".to_string();
let mut f = Foo::default();
f.on_change(|a| {
abc.push_str("."); // PROBLEM HERE; uncomment and it works!
println!("- param: {:?}", a);
});
let param = "a".to_string();
(f.cb)(¶m);
(f.cb)(¶m);
(f.cb)(¶m);
println!("end: {:?}", abc);
}
Expected output:
- param: "a"
- param: "a"
- param: "a"
end: "abc..."
Actual output (compiler error):
error[E0502]: cannot borrow `abc` as immutable because it is also borrowed as mutable
--> src/main.rs:33:27
|
25 | f.on_change(|a| {
| --- mutable borrow occurs here
26 | abc.push_str("."); // PROBLEM HERE; uncomment and it works!
| --- first borrow occurs due to use of `abc` in closure
...
33 | println!("end: {:?}", abc);
| ^^^ immutable borrow occurs here
34 | }
| - mutable borrow might be used here, when `f` is dropped and runs the destructor for type `Foo<'_, String>`
The compiler error is pretty clear and it definitely has something to do with lifetimes. I think my problem is that I need to tell the compiler about the closure and its parameter lifetimes – but the question is, how?
How should I modify code sample #2 to get callback registration work like in code sample #1?
REPL:
abcis still bound inf.Variables get destroyed at the end of the scope they live in.
flives inmain, sofgets destroyed at the end of main, which means that during theprintln,fstill lives and bindsabc.To destroy a variable earlier, you have to possibilities:
dropon itHere with
drop:And here with a nested scope:
Both code snippets print: