trait Container<T> {
// This is more nontrivial (but with the same type signature) in the real code
fn extract(self) -> T;
}
struct DummyContainer<T> {
value: T
}
impl<T> DummyContainer<T> {
pub fn new(val: T) -> DummyContainer<T> {
DummyContainer {value: val}
}
}
impl<T> Container<T> for DummyContainer<T> {
fn extract(self) -> T {
self.value
}
}
to do computations that produce T and place them into Containers.
I tried to implement the trait as follows:
impl Evaluator for DummyEvaluator {
fn compute<T, F> (func: F) -> Box<dyn Container<T>>
where
F: Fn() -> T
{
Box::new(DummyContainer::new(func()))
}
}
However, I get the error error[E0310]: the parameter type T may not live long enough so that the type DummyContainer<T> will meet its required lifetime bounds when trying to compile this. I'm not sure what the issue is, however, since the return value of F is immediately passed into the DummyContainer, which takes ownership of the T and drops the T when it is itself dropped.
How can I add lifetime annotations to resolve this issue? (I suppose another workaround would be cloning the result of func() and adding a T: Clone type bound, but I'd prefer not to do this.)
Edit: Changing the last impl to
let func_val = func();
Box::new(DummyAwaitable::new(func_val.clone()))
The problem here is that Box<dyn Container<T>> is the same as Box<dyn Container<T> + 'static>.
Lifetime bounds S: 'a for a type S basically mean that all the lifetimes that S “contains” (syntactically) are longer than 'a. Something like String: 'a holds for any lifetime 'a, OTOH for a reference type, e.g. &'b str: 'a only is fulfilled if 'b: 'a, i.e. “''b outlives 'a”.
Now as I already indicated above, a trait object type such as dyn Container<T> always also has a lifetime; if it’s not written explicitly then it’s deduced by some “simple” rules, explicit would be to write dyn Container<T> + 'a for some lifetime 'a. In this case when used in a Box<…> the rules work out to make dyn Container<T> mean the same as dyn Container<T> + 'static.
In the general case, a type S can be turned into a trait object dyn Trait + 'a if both S: Trait and S: 'a are fulfilled.
Back to the example. You’re trying, in your fn compute implementation, to turn DummyContainer<T> into dyn Container<T> + 'static (by the way, let’s ignore the fact that this can only happen inside of a box). The requirement DummyContainer<T>: Container<T> is not a problem, but DummyContainer<T>: 'staticis a problem. See, T is a type argument and thus it may syntactically contain any kinds of lifetimes, T might be some type &'b str where 'b is shorter than 'static. However, in such a case, the type DummyContainer<T> would be DummyContainer<&'b str>, which contains the lifetime 'b syntactically as well. This means DummyContainer<&'b str>: 'static is violated if 'b is shorter than 'static.
Now, one way to go about solving the problem is to just restrict T to never contain such lifetimes. This would be written as T: 'static, adding this bound to fn compute (both in the trait and the impl) will probably make your code compile. If that’s to restrictive, you could add a lifetime argument to the trait Evaluator. I.e. create Evaluator<'a> with fn compute<T: 'a, F> (func: F) -> Box<dyn Container<T> + 'a> (where F: Fn() -> T). The approach with a lifetime argument is – in a sense – a generalization of the 'static one, since Evaluator<'static> would then have exactly the 'static bounds that Evaluator has in the other approach.