Description
Code
// Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a508fc33782b233da242b24a6931c14e
use std::collections::HashMap;
struct Inner;
impl Inner {
fn maybe_get(&self) -> Option<&String> {
None
}
}
#[derive(Default)]
struct Foo {
m: HashMap<String, Inner>
}
impl Foo {
fn get<R>(&self, retrieve: impl FnMut(&Inner) -> Option<R>) -> Option<R> {
self.m.values().find_map(retrieve)
}
}
fn main() {
let f = Foo::default();
f.get(Inner::maybe_get);
}
Current output
// Stable 1.69
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:23:5
|
23 | f.get(Inner::maybe_get);
| ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected trait `for<'a> <for<'a> fn(&'a Inner) -> Option<&'a String> {Inner::maybe_get} as FnOnce<(&'a Inner,)>>`
found trait `for<'a> <for<'a> fn(&'a Inner) -> Option<&'a String> {Inner::maybe_get} as FnOnce<(&'a Inner,)>>`
note: the lifetime requirement is introduced here
--> src/main.rs:16:54
|
16 | fn get<R>(&self, retrieve: impl FnMut(&Inner) -> Option<R>) -> Option<R> {
| ^^^^^^^^^
// Nightly 1.71.0 2023-05-11
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:23:5
|
23 | f.get(Inner::maybe_get);
| ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected enum `Option<&String>`
found enum `Option<&String>`
note: the lifetime requirement is introduced here
--> src/main.rs:16:54
|
16 | fn get<R>(&self, retrieve: impl FnMut(&Inner) -> Option<R>) -> Option<R> {
| ^^^^^^^^^
Desired output
Should mention that the lifetimes of self
, retrieve
and R
must match.
The code compiles if you write get
as
fn get<'a, R>(&'a self, retrieve: impl FnMut(&'a Inner) -> Option<R>) -> Option<R>
where R: 'a
Rationale and extra context
No response
Other cases
The original code compiles fine when invoked with a closure like f.get(|_i| None::<&String>);
that does not relate input and output lifetimes.
Calling get
with a call to maybe_get
as a closure (that does relate the lifetimes) like f.get(|i| i.maybe_get())
gives
Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
--> src/main.rs:25:15
|
25 | f.get(|i| i.maybe_get());
| -- ^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| ||
| |return type of closure is Option<&'2 String>
| has type `&'1 Inner`
which I have mixed feelings about: on the one hand, you know from the error that the problem is the relation between the lifetimes.
On the other, you could write maybe_get
as fn maybe_get<'s>(&'s self) -> Option<&'s String>
and still get the same error, even though the lifetimes are now the same in the definition (the actual problem is that get
doesn't know this about R
).
Writing get
as
fn get<'a, R>(&self, retrieve: impl FnMut(&'a Inner) -> Option<R>) -> Option<R>
where R: 'a
(note that the reference to Inner
and R
are constrained through 'a
, but not self
) gives
Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
--> src/main.rs:19:9
|
16 | fn get<'a, R>(&self, retrieve: impl FnMut(&'a Inner) -> Option<R>) -> Option<R>
| -- - let's call the lifetime of this reference `'1`
| |
| lifetime `'a` defined here
...
19 | self.m.values().find_map(retrieve)
| ^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a
which is much more helpful in figuring out that one needs to bound self
.
Anything else?
No response