I have implemented the observer pattern in Rust. In my implementation, observers are just callback functions, and observables are things that can store optional callback functions, and they call them whenever the associated setter is called.
See below:
mod data {
pub struct Point {
//private fields, must be accessed through getter and setter
x: i32,
on_x_changed: Option<Box<dyn Fn(&i32)>>,
y: i32,
on_y_changed: Option<Box<dyn Fn(&i32)>>,
}
impl Point {
pub fn new() -> Point {
Point {
x: 0,
on_x_changed: None,
y: 0,
on_y_changed: None
}
}
pub fn x(&self) -> &i32 {
&self.x
}
pub fn set_x(&mut self, x: i32) {
if x == self.x {return;}
self.x = x;
if let Some(f) = &self.on_x_changed {
f(&self.x);
}
}
pub fn y(&self) -> &i32 {
&self.y
}
pub fn set_y(&mut self, y: i32) {
if y == self.y {return;}
self.y = y;
if let Some(f) = &self.on_y_changed {
f(&self.y);
}
}
pub fn set_on_x_changed(&mut self, f: Box<dyn Fn(&i32)>) {
self.on_x_changed = Some(f);
}
pub fn set_on_y_changed(&mut self, f: Box<dyn Fn(&i32)>) {
self.on_y_changed = Some(f);
}
}
}
This works for simple examples, but I want to set the on_x_changed
callback to a closure that borrows a captured item. This gives an error:
fn main() {
//observable
let mut p = (data::Point::new(), data::Point::new());
//observer
let on_p0_x_changed = |p0_x: &i32| {
println!("{}", p0_x);
println!("{}", p.1.x()); //p.1 is captured
//check if p0_x collides with p1_x...
};
p.0.set_on_x_changed(Box::new(on_p0_x_changed)); // on_p0_x_changed closure lives as long as p, beause p owns it
// p.1.set_x(1);
// p.0.set_x(1);
}
error[E0597]: `p.1` does not live long enough
--> src\main.rs:55:33
|
54 | let on_p0_x_changed = |p0_x: &i32| {
| ------------ value captured here
55 | println!("{} {}", p0_x, p.1.x());
| ^^^ borrowed value does not live long enough
...
59 | p.0.set_on_x_changed(Box::new(on_p0_x_changed));
| ------------------------- cast requires that `p.1` is borrowed for `'static`
...
65 | }
| - `p.1` dropped here while still borrowed
If I give the on_x_changed
a lifetime annotations, I get a different error.
pub struct Point<'a> {
//private feilds, must be accessed through getter and setter
x: i32,
on_x_changed: Option<Box<dyn Fn(&i32) + 'a>>,
y: i32,
on_y_changed: Option<Box<dyn Fn(&i32) + 'a>>,
}
//and also update the Impl with <'a>...
error[E0597]: `p.1` does not live long enough
--> src\main.rs:58:24
|
56 | let on_p0_x_changed = |p0_x: &i32| {
| ------------ value captured here
57 | println!("{}", p0_x);
58 | println!("{}", p.1.x()); //p.1 is captured
| ^^^ borrowed value does not live long enough
...
67 | }
| -
| |
| `p.1` dropped here while still borrowed
| borrow might be used here, when `p` is dropped and runs the destructor for type `(Point<'_>, Point<'_>)`
I'm not super confident with lifetime annotations, but I'm pretty sure all they do is add constraints, and the above example just constrains the callbacks that I can assign to on_x_changed and on_y_changed to have equal lifetimes. Is this correct? Whatever the case, it gives a more readable error message.
It suggests that p.1
might still be used in the on_p0_x_changed
closure , when p
is dropped at the end of main.
However it is pretty clear that the on_p0_x_changed
closure will be dropped at the same time a p
, because p
owns the on_p0_x_changed
closure. So p.1
cannot be used after p
is dropped.
Is there a way to help the compiler understand this?