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

Unhelpful error message using impl trait to return a closure. #38615

Closed
peterjoel opened this issue Dec 26, 2016 · 5 comments
Closed

Unhelpful error message using impl trait to return a closure. #38615

peterjoel opened this issue Dec 26, 2016 · 5 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@peterjoel
Copy link
Contributor

peterjoel commented Dec 26, 2016

The following code (Rust Playground link)

fn create_recorder<'a>(log: &'a mut Vec<String>) -> impl FnMut(&'a String) {
    let handler = |msg: &String| -> () {
        log.push(format!("The messages was: {}", msg));
    };
    handler
}

produces the error:

rustc 1.15.0-nightly (71c06a56a 2016-12-18)
error[E0564]: only named lifetimes are allowed in `impl Trait`, but `` was found in the type `[closure@<anon>:4:19: 6:6 log:&mut &'a mut std::vec::Vec<std::string::String>]`
 --> <anon>:3:53
  |
3 | fn create_recorder<'a>(log: &'a mut Vec<String>) -> impl FnMut(&'a String) {
  |                                                     ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

This error can be gotten rid of, by adding a lifetime constraint:

fn create_recorder<'a>(log: &'a mut Vec<String>) -> impl FnMut(&'a String) + 'a {

But that still leaves an error about borrowing the log reference:

error[E0373]: closure may outlive the current function, but it borrows `log`, which is owned by the current function

Fixing this (by making the closure a move, as is suggested) doesn't require the named lifetime, so really the error second error message should be the only one presented.

@steveklabnik steveklabnik added the A-diagnostics Area: Messages for errors, warnings, and lints label Dec 26, 2016
@steveklabnik steveklabnik added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Mar 9, 2017
@Seeker14491
Copy link
Contributor

I ran into this too, but returning an iterator. In my case adding the lifetime is required in addition to marking the closure move.

Playground

fn apply_permutation<'a, T>(
    objects: &'a [T],
    permutation: &'a [usize],
) -> impl Iterator<Item = T> + 'a
where
    T: Copy,
{
    permutation.iter().map(move |&i| objects[i])
}

@Mark-Simulacrum Mark-Simulacrum added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jul 26, 2017
@gnzlbg
Copy link
Contributor

gnzlbg commented Aug 30, 2017

I ran into this too while trying to return a generator...

#![feature(generators, generator_trait, conservative_impl_trait)]
use std::ops::{Generator};

struct Stack { 
    x: i32
}

impl Stack {
    fn new(x: i32) -> Stack { Stack{x: x} }
    fn gen(& mut self) -> impl Generator<Yield=i32,Return=()> { 
 // fn gen<'a>(&'a mut self) -> impl Generator<Yield=i32,Return=()> + 'a { 
        move || { for _i in 0..10 { self.x = self.x + 1; yield self.x; } return (); } 
    }
}

(The commented out line is the correct signature)

The error message is not useful, at all:

   Compiling playground v0.0.1 (file:///playground)
error[E0564]: only named lifetimes are allowed in `impl Trait`, but `` was found in the type `[generator@src/main.rs:13:9: 13:86 self:&mut Stack ((), std::ops::Range<i32>, i32, fn(std::ops::Range<i32>) -> <std::ops::Range<i32> as std::iter::IntoIterator>::IntoIter {<std::ops::Range<i32> as std::iter::IntoIterator>::into_iter}, Stack)]`
  --> src/main.rs:11:27
   |
11 |     fn gen(& mut self) -> impl Generator<Yield=i32,Return=()> { 
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This error message is so bad that it should IMO block the stabilization of conservative impl trait (@withoutboats).

Also, when I click the E0564 on the playground it sends me to a page that doesn't contain an explanation for it. Maybe that is a playground bug, and the playground should be sending me to some nightly page... Anyhow, this doesn't make this any better.

@gnzlbg
Copy link
Contributor

gnzlbg commented Aug 30, 2017

The issues #43719 and #39722 seem to be duplicates of this one.

I've just rechecked, and the playground does indeed have a bug (rust-lang/rust-playground#207) in that the playground always links to the stable error explanations and not to the nightly ones when compiling with nightly.

However, the error at hand here, E0564, doesn't have an explanation at all.

Trying to return closures/generators, and trying to return iterators, seem to be the most common cause of triggering this error in all the issues currently filled, so maybe an explanation should cover that.

@cramertj
Copy link
Member

cramertj commented Jan 5, 2018

This has been fixed.

@estebank
Copy link
Contributor

estebank commented Jul 15, 2018

Current output:

error[E0373]: closure may outlive the current function, but it borrows `log`, which is owned by the current function
 --> src/main.rs:5:19
  |
5 |     let handler = |msg: &String| -> () {
  |                   ^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `log`
6 |         log.push(format!("The messages was: {}", msg));
  |         --- `log` is borrowed here
help: to force the closure to take ownership of `log` (and any other referenced variables), use the `move` keyword
  |
5 |     let handler = move |msg: &String| -> () {
  |                   ^^^^^^^^^^^^^^^^^^^^^^^^^

The suggestion is correct.


#38615 (comment) now compiles.


#38615 (comment) now suggests the correct fix:

error: cannot infer an appropriate lifetime
  --> src/main.rs:12:9
   |
10 |     fn gen(& mut self) -> impl Generator<Yield=i32,Return=()> { 
   |                           ----------------------------------- this return type evaluates to the `'static` lifetime...
11 |  // fn gen<'a>(&'a mut self) -> impl Generator<Yield=i32,Return=()> + 'a { 
12 |         move || { for _i in 0..10 { self.x = self.x + 1; yield self.x; } return (); } 
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...but this borrow...
   |
note: ...can't outlive the anonymous lifetime #1 defined on the method body at 10:5
  --> src/main.rs:10:5
   |
10 | /     fn gen(& mut self) -> impl Generator<Yield=i32,Return=()> { 
11 | |  // fn gen<'a>(&'a mut self) -> impl Generator<Yield=i32,Return=()> + 'a { 
12 | |         move || { for _i in 0..10 { self.x = self.x + 1; yield self.x; } return (); } 
13 | |     }
   | |_____^
help: you can add a constraint to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the method body at 10:5
   |
10 |     fn gen(& mut self) -> impl Generator<Yield=i32,Return=()> + '_ { 
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

7 participants