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