Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Confusing lifetime error with closure #42758

Open
dimbleby opened this issue Jun 19, 2017 · 6 comments
Open

Confusing lifetime error with closure #42758

dimbleby opened this issue Jun 19, 2017 · 6 comments
Labels
A-closures Area: closures (`|args| { .. }`) A-lifetimes Area: lifetime related C-enhancement Category: An issue proposing an enhancement or a PR with one.

Comments

@dimbleby
Copy link

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!

@Rufflewind
Copy link
Contributor

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.

@bossmc
Copy link
Contributor

bossmc commented Jun 20, 2017

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.

@vitalyd
Copy link

vitalyd commented Jun 20, 2017

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.

@dimbleby
Copy link
Author

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.

@vitalyd
Copy link

vitalyd commented Jun 20, 2017

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.

@frewsxcv frewsxcv added A-closures Area: closures (`|args| { .. }`) A-lifetimes Area: lifetime related labels Jun 21, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jul 27, 2017
@mbrubeck
Copy link
Contributor

Related: #22340 and #58052.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: closures (`|args| { .. }`) A-lifetimes Area: lifetime related C-enhancement Category: An issue proposing an enhancement or a PR with one.
Projects
None yet
Development

No branches or pull requests

7 participants