Search code examples
rusttraitslifetime

Why must T be 'static when cloning into Rc<RefCell<dyn Trait>>?


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.


Solution

  • 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
        }
    }
    

    (Playground)