Consider this code:
struct WithLifetime<'a> {
s: &'a str
}
impl WithLifetime<'_> {
fn in_impl(&self) -> bool {
self.s == "a"
}
}
fn out_of_impl(wl: &WithLifetime<'_>) -> bool {
wl.s == "a"
}
fn higher_order(f: fn(&WithLifetime<'_>) -> bool) -> bool {
let s = "a";
let wl = WithLifetime { s };
f(&wl)
}
fn main() {
higher_order(out_of_impl); // This line compiles
higher_order(WithLifetime::in_impl); // This line does not
}
The final line of main
fails to compile with this error:
error[E0308]: mismatched types
--> src/main.rs:23:18
|
23 | higher_order(WithLifetime::in_impl); // This line does not
| ^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'r, 's> fn(&'r WithLifetime<'s>) -> _`
found fn pointer `for<'r> fn(&'r WithLifetime<'_>) -> _`
As far as I can figure out, in_impl
and out_of_impl
should be exactly the same function. They both take a reference to a WithLifetime
, and don't care about the lifetime of that reference or the lifetime parameter of the WithLifetime
instance. Both should be valid parameters to pass to higher_order
. The compiler does not agree.
What is the problem with passing in_impl
to higher_order
, and why doesn't the compiler allow this? What can I do to pass struct methods on structs with lifetime parameters to higher-order functions?
First, let's find out the types for all the 3 functions:
fn main() {
let x: i32 = out_of_impl;
let x: i32 = WithLifetime::in_impl;
let x: i32 = higher_order;
}
Type of out_of_impl
:
for<'r, 's> fn(&'r WithLifetime<'s>) -> bool {out_of_impl}
Type of WithLifetime::in_impl
:
for<'r> fn(&'r WithLifetime<'_>) -> bool {WithLifetime::<'_>::in_impl}
Type of higher_order
:
fn(for<'r, 's> fn(&'r WithLifetime<'s>) -> bool) -> bool {higher_order}
higher_order
accepts a function in which both the lifetimes aren't named until the function is called. So it basically accepts a function which works for any lifetimes 'r
and 's
. out_of_impl
satisfies that criteria.
But in case of WithLifetime::in_impl
, the inner lifetime needs to be known beforehand for<'r> fn(&'r WithLifetime<'_>)
.
To pass WithLifetime::in_impl
, you would need to change it to:
fn in_impl<'b, 'c>(abc: &'b WithLifetime<'c>) -> bool {
abc.s == "a"
}
Now, this function works for any arbitrary lifetimes 'b
and 'c
.
To accept in_impl
without changing it, as @ Milan did,
fn higher_order<'b>(f: fn(&WithLifetime<'b>) -> bool) -> bool {
let s = "a";
let wl = WithLifetime { s };
f(&wl)
}
Now, higher_order
has a type:
for<'b> fn(for<'r> fn(&'r WithLifetime<'b>) -> bool) -> bool {higher_order}
It accepts a function where lifetime 'r
is defined only when the function is called and liftime 'b
is known beforehand.
This works for out_of_impl
because it accepts any arbitrary lifetimes. Also works for in_impl
because now the signature matches higher_order
.
How does for<> syntax differ from a regular lifetime bound? has a pretty nice explaination for HRTB.
In higher_order
function, wl
is dropped before it is borrowed, instead, change that function to this
fn higher_order<'b>(f: fn(&WithLifetime<'b>) -> bool) -> bool {
let s = "a";
let wl= WithLifetime { s };
f(&wl)
}
EDIT:
In simple words, fn higher_order<'b>(f: fn(&WithLifetime<'b>) -> bool) -> bool
this 'b means that higher_order
body lifetime will match fn
lifetime.
Otherwise, if fn
don't care what lifetime higher_order
has, and this implicitly means wl
value too since it is the body of high_order function, then compiler can drop wl
at the same line it is declared since point is to save memory, right?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.