rustlifetimetrait-objects

How to add lifetime Parameter to Box<> with dyn type alias


I have an type alias for a Fn trait like type SomeSub = dyn for<'a> Fn(&'a str) -> &'a str; which I would like to use with a Box with an explicit lifetime like Box<SomeSub + 'b>. Unfortunately this doesn't compile:

type SomeSub = dyn for<'a> Fn(&'a str) -> &'a str;

struct SomeBuilder(pub usize);

impl SomeBuilder {
    // --> this gets rejected with "type aliases cannot be used as traits"
    fn get_closure<'o>(
        &'o self
    ) -> Box<dyn SomeSub + 'o> {
        Box::new(|s| &s[self.0..])
    }

    // This works, but duplicates the code of the type alias
    fn get_closure_long<'o>(
        &'o self
    ) -> Box<dyn for<'a> Fn(&'a str) -> &'a str + 'o> {
        Box::new(|s| &s[self.0..])
    }
}

While the second method get_closure_long() compiles, get_closure() results in the error:

error[E0404]: expected trait, found type alias `SomeSub`
 --> src/lib.rs:8:18
  |
8 |     ) -> Box<dyn SomeSub + 'o> {
  |                  ^^^^^^^ type aliases cannot be used as traits

Leaving out the dyn like -> Box<SomeSub + 'o> will be rejected with "type aliases cannot be used as traits". Using Box<SomeSub> works bot doesn't allow the closure to capture any references.

What is the correct way to combine a closure type alias with a Box and an explicit lifetime for that box?


Solution

  • You can make the type alias generic:

    type SomeSub<'b> = dyn for<'a> Fn(&'a str) -> &'a str + 'b;
    

    Then SomeSub<'o> is a valid type that includes the lifetime, so this compiles:

    fn get_closure<'o>(&'o self) -> Box<SomeSub<'o>> {
        Box::new(|s| &s[self.0..])
    }
    

    Playground

    Alternatively, you can emulate a trait alias using the technique shown in this answer:

    trait SomeSub: for<'a> Fn(&'a str) -> &'a str {}
    
    impl<T> SomeSub for T where T: for<'a> Fn(&'a str) -> &'a str {}
    

    Then your original get_closure() that returns Box<dyn SomeSub + 'o> compiles.

    Playground