Here is my code; in Ref::computed
I get an error that F
and U
must be 'static
:
use std::cell::RefCell;
use std::rc::Rc;
trait Observer<T> {
fn update(&mut self, v: &T);
}
struct Computed<U, F> {
value: U,
callback: F,
}
impl<T, U, F> Observer<T> for Computed<U, F> where F: Fn(&T) -> U {
fn update(&mut self, v: &T) {}
}
struct Ref<T> {
value: T,
observers: Vec<Rc<RefCell<dyn Observer<T>>>>,
}
impl<T> Ref<T> {
fn computed<U, F>(&mut self, callback: F) -> Rc<RefCell<Computed<U, F>>>
where
F: Fn(&T) -> U,
{
let s = Rc::new(RefCell::new(Computed {
value: callback(&self.value),
callback,
}));
self.observers.push(s.clone());
s
}
}
error[E0310]: the parameter type `F` may not live long enough
--> src/lib.rs:34:29
|
34 | self.observers.push(s.clone());
| ^^^^^^^^^
| |
| the parameter type `F` must be valid for the static lifetime...
| ...so that the type `F` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
28 | F: Fn(&T) -> U + 'static,
| +++++++++
error[E0310]: the parameter type `U` may not live long enough
--> src/lib.rs:34:29
|
34 | self.observers.push(s.clone());
| ^^^^^^^^^
| |
| the parameter type `U` must be valid for the static lifetime...
| ...so that the type `U` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
26 | fn computed<U: 'static, F>(&mut self, callback: F) -> Rc<RefCell<Computed<U, F>>>
| +++++++++
If I do as the error suggest then everything works out.
I just can't figure out why. Since the smart ptr has ownership of F
, why is it necessary to declare the lifetime? And it has to be 'static
? If someone could explain, I'd appreciate it.
The error is unrelated to the call to clone
. Rather, it's because you are attempting to store the value in self.observers
.
When you say dyn Trait
without specifying a lifetime, an implicit lifetime is added, which in this case is 'static
, so it's as though you'd written dyn Trait + 'static
. Therefore, the type of the observers
field of Ref
is effectively Vec<Rc<RefCell<dyn Observer<T> + 'static>>>
. This is why the compiler is forcing you to add 'static
constraints, so that the type you're coercing into dyn Observer<T>
satisfies the implicit 'static
lifetime bound.
If you want to allow the trait object to contain a non-static lifetime, you have to annotate it as such:
struct Ref<'a, T> {
value: T,
observers: Vec<Rc<RefCell<dyn Observer<T> + 'a>>>,
}
Once you've done this, you then need to add appropriate lifetime annotations to your impl Ref
block. Specifically, in Ref::computed
you must add bounds to constrain F
and U
to live at least as long as the lifetime parameter of Ref
:
impl<'a, T> Ref<'a, T> {
fn computed<U, F>(&mut self, callback: F) -> Rc<RefCell<Computed<U, F>>>
where
F: Fn(&T) -> U + 'a,
U: 'a,
{
let s = Rc::new(RefCell::new(Computed {
value: callback(&self.value),
callback,
}));
self.observers.push(s.clone());
s
}
}