genericsrustlifetimenightly-build

Chaining functions on a trait


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).


Solution

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