Fn that accepts closure that returns a &str or a String

Is it possible to write a function that accepts a closure that will returns a &str or a String?

struct Data<'a> {
    data: &'a str,
}

type GetterStr    = Box<dyn for<'r,'e> Fn(&'r Data<'e>) -> &'e str>;
type GetterString = Box<dyn for<'r,'e> Fn(&'r Data<'e>) -> String>;
type Getter<T>    = Box<dyn for<'r,'e> Fn(&'r Data<'e>) -> T>;

fn fun1(d: &Data, g: &GetterStr) {
    println!("{}", g(d));
}

fn fun2(d: &Data, g: &GetterString) {
    println!("{}", g(d));
}

fn fun3<T: AsRef<str>>(d: &Data, g: &Getter<T>) {
    println!("{}", g(d).as_ref());
}

fn main() {
    let data = "hello world";
    let data = Data { data };

    let get1: GetterStr = Box::new(|d: &Data| d.data);
    fun1(&data, &get1);

    let get2: GetterString = Box::new(|d: &Data| d.data.to_uppercase());
    fun2(&data, &get2);
    
    // Fails with: expected concrete lifetime, found bound lifetime parameter 'e
    //fun3(&data, &get1);
    
    fun3(&data, &get2);
}

In the above fun3 fails to accept the GetterStr argument. The compiler complains with "expected concrete lifetime, found bound lifetime parameter 'e".

type Getter<T> = Box<dyn for<'r,'e> Fn(&'r Data<'e>) -> T + e> is not acceptable syntax and type Getter<T> = Box<dyn for<'r,'e> Fn(&'r Data<'e>) -> T<'e>> does not work either.

Is there a way to specify that the returned generic has a lifetime of of at least 'e?

You can't because bounds on for<'lifetimes> statements aren't valid in that context. You could move the lifetime out of the for clause though. Because you've got a type declaration it's easy:

type Getter<T>     = Box<dyn for<'r,'e> Fn(&'r Data<'e>) -> T>;
// To
type Getter<'e, T> = Box<dyn for<'r> Fn(&'r Data<'e>) -> T>;

Then, we can say:

fn fun3<'a: 'b + 'c, 'b, 'c, T: AsRef<str>>(d: &'a Data<'b>, g: &'c Getter<'b, T>) {
    println!("{}", g(d).as_ref());
}

More clearly:

fn fun3<
    'data: 'my_str + 'function,
    'my_str,
    'function,
    T: AsRef<str>
>(d: &'data Data<'my_str>, g: &'function Getter<'my_str, T>) {
    println!("{}", g(d).as_ref());
}

Such that you can substitute 'my_str into the Fn declaration without ever having to use a for clause.


Playground

Take a look at std::borrow::Cow<'a, str>. That probably do what you need.

Thank you. Taking advantage of lifetime elision, I simplified it to:

struct Data<'a> { data: &'a str, }

type GetterStr<'e>  = Box<dyn Fn(&Data<'e>) -> &'e str>;
type GetterString   = Box<dyn Fn(&Data) -> String>;
type Getter<'e, T>  = Box<dyn Fn(&Data<'e>) -> T>;

fn fun3<'b, T: AsRef<str>>(d: &Data<'b>, g: &Getter<'b, T>) {
    println!("{}", g(d).as_ref());
}

fn main() {
    let data = Data { data: "hello world" };
    let get1: GetterStr    = Box::new(|d: &Data| d.data);
    let get2: GetterString = Box::new(|d: &Data| d.data.to_uppercase());
    fun3(&data, &get1);    
    fun3(&data, &get2);
}

It still feels odd that the lifetime of the return type has to be bound at the function call rather than being able to specify it via a higher-rank trait bounds, but this has been pretty helpful.

Thanks!

Yes, this, I believe, is because the one that returns a string knows it does not have any tied lifetimes, the one with a str does have tied lifetimes, but the ambiguity of there being either or none (as is the case when using generics) is when the compiler gets confused.