This is a follow up of "dropped here while still borrowed" when making lifetime explicits but can be looked at independently.
Now that @jthulhu made me discover the Higher-Rank Trait Bounds that fixed my previous error, I want to go a step further by generalizing it. So let's create a trait :
trait ToImplement {
type Arg<'arg>;
fn do_something<F>(&self, f: F)
where
F: for<'b> FnOnce(Self::Arg<'b>) -> ();
}
This trait can be implemented without an issue and, when use directly, works as expected as this test shows :
impl ToImplement for String {
type Arg<'arg> = &'arg str;
fn do_something<F, T>(&self, f: F) -> T
where
F: for<'b> FnOnce(Self::Arg<'b>) -> T,
{
f(&self)
}
}
#[test]
fn works() {
let hello = String::from("Hello");
let r = hello.do_something(|s| format!("{s} world!"));
assert_eq!(r,"Hello world!")
}
Now, let's try to write a function that use the method do_something
without knowing the implementer but with knowing some constraints on the Arg
type so we can use it.
10 │ fn go<D, I>(implementor: I)
11 │ where
12 │ D: Display,
13 │ for<'a> I: ToImplement<Arg<'a> = D>,
14 │ {
15 │ implementor.do_something(|a| println!("{a}"));
16 │ }
This does compile properly but if we try to use it like this :
33 │ #[test]
34 │ fn works() {
35 │ let hello = String::from("Hello");
36 │ go(hello);
37 │ }
Then we get this error :
error[E0308]: mismatched types
|
36 | go(hello);
| ^^^^^^^^^ one type is more general than the other
|
= note: expected reference `&'a str`
found reference `&str`
note: the lifetime requirement is introduced here
|
13 | for<'a> I: ToImplement<Arg<'a> = D>,
|
I think the way I declare the lifetime 'a
on line 13 is wrong. But I have no idea on how else I could do it.
I read the rustonomicon chapter about ownership that @jthulhu pointed me to and thought that Borrow splitting would get me some answers and looked at the implementation of Take but there is no Higher-Rank Trait Bounds.