I'm trying to make chainable transformations on a trait, and I'm having some issues.
I have a bunch of transformation functions of the form:
fn transform<T: MyTrait>(in: T) -> impl MyTrait
And I want a function chain
which will allow me to do
let mut val: Box<MyTrait> = ...;
val = chain(val, transform1);
val = chain(val, transform2);
...
I have written this function
fn chain<T, U, F>(val: Box<T>, f: F) -> Box<MyTrait>
where T: MyTrait,
U: MyTrait,
F: FnOnce(T) -> U {
Box::new(f(*val))
}
But when I compile, the borrow checker tells me that the type parameter U doesn't live long enough. I'm pretty sure my trait bounds are what I want, and I've tried various things with lifetime specifiers, so I'm stuck :(
P.S. : Is it possible to make the chain
function generic on MyTrait
? I don't think it's possible, but we never know...
EDIT:
I've added the fix proposed by @chris-emerson in his answer, by as I've said in its comments, I have uncovered another problem which seems unresolvable.
Here is a gist of the code, to not clutter this post.
In short, the problem is: the chain function needs to dereference the Box<T>
object and pass the T
to the transform function, so T
must be Sized
. But the whole point of this function was to allow arbitrary (and unknown at compile-time) MyTrait
implementations to be used. For example:
let mut val: Box<MyTrait> = ...;
//here we can know the type inside the Box
if ... {
val = chain(val, transform);
}
//but here we don't know anymore
//(its either the original type,
//or the type returned by transform)
So this design can't work unless the transform function can take a &T or a &mut T (which it can't, as I need to consume the input to produce the output).
The full compiler message is:
error[E0310]: the parameter type `U` may not live long enough
--> <anon>:7:3
|
7 | Box::new(f(*val))
| ^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `U: 'static`...
note:...so that the type `U` will meet its required lifetime bounds
--> <anon>:7:3
|
7 | Box::new(f(*val))
| ^^^^^^^^^^^^^^^^^
error: aborting due to previous error
The compiler is saying that it needs U
to live for the 'static
lifetime; what this really means is that any references inside it need to be valid for that lifetime, since the Box
can live forever (as far as the compiler knows here).
So the fix is simple: add 'static
to U
's bounds:
fn chain<T, U, F>(val: Box<T>, f: F) -> Box<MyTrait>
where T: MyTrait,
U: MyTrait + 'static,
F: FnOnce(T) -> U,
{
Box::new(f(*val))
}
Adding an extra bound U: 'static
would also be equivalent.