Confusing lifetime error with closure
struct Foo<'a> { foo: &'a str }
fn main() {
let foo = Foo { foo: "foo" };
let closure = |foo: Foo| foo;
closure(foo);
}
Intuitively I don't quite see why this shouldn't compile, but:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> <anon>:5:30
|
5 | let closure = |foo: Foo| foo;
| ^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 5:19...
--> <anon>:5:19
|
5 | let closure = |foo: Foo| foo;
| ^^^^^^^^^^^^^^
note: ...so that expression is assignable (expected Foo<'_>, found Foo<'_>)
--> <anon>:5:30
|
5 | let closure = |foo: Foo| foo;
| ^^^
note: but, the lifetime must be valid for the call at 6:5...
--> <anon>:6:5
|
6 | closure(foo);
| ^^^^^^^^^^^^
note: ...so type `Foo<'_>` of expression is valid during the expression
--> <anon>:6:5
|
6 | closure(foo);
| ^^^^^^^^^^^^
Especially bewildering is that expected Foo<'_>, found Foo<'_>. Those look the same to me!
This is a really weird error. It seems the compiler is failing to infer the lifetimes for the closure correctly. My naive intuition is something like:
for<'a> Fn(Foo<'a>) -> Foo<'a>which matches what is normally done for elision in ordinary functions. However that does not seem to be the case here. It appears to be inferring something like:
Fn(Foo<'__1>) -> Foo<'__1>for some pre-chosen lifetime
'__1. But that wouldn't make sense because lifetimes isn't supposed to cross function boundaries…Especially bewildering is that
expected Foo<'_>, found Foo<'_>. Those look the same to me!Yeah, those error messages are very frustrating to read. It would be nice if the compiler could actually assign names to these lifetimes.
Phil Ruffwind at 2017-06-19 22:23:03
As an experiment, if you use a function rather than a closure, even if you rely on lifetime inference for the inner lifetime of
Foo, it compiles happily. See https://is.gd/wFQX2d.Andy Caldwell at 2017-06-20 10:32:56
I don't think lifetime elision in closures works (or is even present?). Generally, closures work best when no type ascriptions are provided and the compiler works out everything itself (the type and associated lifetimes). But, you could define this closure like this:
let closure = |foo| -> Foo { foo };The compiler is able to infer the appropriate lifetime in the output position here, and the code compiles.vitalyd at 2017-06-20 11:47:08
Thanks, interesting. In that example the compiler still infers some concrete lifetime, IIUC, in contrast with what happens in the function. But this time it manages to find a lifetime that actually works.
However you look at it, this is all very confusing! I still don't know what the actual rules are beyond: experiment and hope for the best, and if you can't make it work then don't use a closure.
So:
- if the compiler can be persuaded to do better lifetime inference for closures so that people don't need to know this stuff, that would be great
- else, suggest at least that improving the error messages would be helpful - perhaps an explanation (or link to some resource containing an explanation) saying that lifetime inference for closures doesn't work as you might have expected and suggesting workarounds.
David Hotham at 2017-06-20 12:00:39
I agree, it's confusing and seemingly inconsistent with how inference/lifetimes work in other contexts.
There have been various proposals to enhance closure syntax to allow specifying lifetimes (and also have inference), but I don't know what the latest/current thinking is.
vitalyd at 2017-06-20 12:20:02
Related: #22340 and #58052.
Matt Brubeck at 2020-12-17 22:56:22