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

"expected &&str, found &&str" and missing context #57189

Closed
AaronKutch opened this issue Dec 29, 2018 · 3 comments
Closed

"expected &&str, found &&str" and missing context #57189

AaronKutch opened this issue Dec 29, 2018 · 3 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: Lifetimes / regions C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@AaronKutch
Copy link
Contributor

AaronKutch commented Dec 29, 2018

Edit: there is a simpler example I found below in the comments

I was trying to solve a lifetime issue another Rustacean had on the Reddit question thread and found this:

use std::collections::HashMap;
use std::thread;

fn get_chunks(string: &str, num: usize) -> Vec<&str> {Vec::new()}

pub fn count_words(string: &str)
                   -> HashMap<&str,usize> {HashMap::new()}

pub fn count_words_parallel(string: &str, threads: usize)
                            -> HashMap<&str,usize> {
    let result = HashMap::new();
    let chunks = get_chunks(string.clone(), threads);
    let mut handles = Vec::with_capacity(chunks.len());

    for chunk in chunks {
        handles.push(thread::spawn(|| {
            count_words(&chunk)
        }));
    }

    for handle in handles {
        if let Ok(counts) = handle.join() {
            for (word, count) in counts {
                match result.get(word) {
                    Some(original) => result.insert(word, original + count),
                    None => result.insert(word, count)
                };
            }
        }
    }

    return result;
}

which triggers this gem (on rustc 1.44.0-nightly (6dee5f112 2020-04-06)):

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src\main.rs:12:36
   |
12 |     let chunks = get_chunks(string.clone(), threads);
   |                                    ^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the function body at 9:1...
  --> src\main.rs:9:1
   |
9  | / pub fn count_words_parallel(string: &str, threads: usize)
10 | |                             -> HashMap<&str,usize> {
11 | |     let result = HashMap::new();
12 | |     let chunks = get_chunks(string.clone(), threads);
...  |
32 | |     return result;
33 | | }
   | |_^
note: ...so that the types are compatible
  --> src\main.rs:12:36
   |
12 |     let chunks = get_chunks(string.clone(), threads);
   |                                    ^^^^^
   = note: expected  `&&str`
              found  `&&str`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `std::collections::HashMap<&str, usize>` will meet its required lifetime bounds
  --> src\main.rs:16:22
   |
16 |         handles.push(thread::spawn(|| {
   |                      ^^^^^^^^^^^^^

error: aborting due to previous error

besides the title, there are other problems with the error reporting that happens with and without that clone:

Just to make sure I am interpreting the problem as a whole right, is the lifetime of the &str passed to count_words_parallel getting elided along until it gets into the new threads via &chunk? The lifetimes of the new threads are 'static which means that lifetime all the way back at the &str must also be 'static or alternatively something has to happen along the way to split up lifetimes (which I was trying to do above via cloneing).

The important point is that I had to work through all the lifetimes myself by prompting the compiler through other errors (in which it will give more info) to figure out what was what. Hindsight of course is 20/20 and now I can see the flow of this particular error message, this is what I think it is trying to tell me:

note: first, the lifetime cannot outlive the anonymous lifetime "the lifetime" refers to the hashmap at the end of the lifetime elision chain mentioned at the end of the error.

but, the lifetime must be valid for the static lifetime I actually don't know what the first "lifetime" is but the "static lifetime" must be the thread spawning closure which is never pointed to (via the really helpful ascii lines and arrows).

so that the type std::collections::HashMap<&str, usize> will meet its required lifetime bounds "required" as in required by the lifetime of the thread spawning closure that was indirectly mentioned above this part of the error. "bounds" as in 'static

The biggest problem I have always had with Rustc's error system when I get stuck is that a key piece of information is being withheld from me, and I believe it would help at this point to be more verbose and have redundancy to the wording. Perhaps have an error format that, for each lifetime involved, describes the where and what of the relevant variables and their lifetimes (saying stuff like "variable x has lifetime # 3, beginning and ending here"), and only after that describes what and how lifetimes collide (saying stuff like "this variable (having lifetime # 3) outlives this other variable (having lifetime # 2) via scoping or closures or whatnot at this place").

@Centril Centril added the A-diagnostics Area: Messages for errors, warnings, and lints label Dec 29, 2018
@estebank
Copy link
Contributor

CC #18759

@estebank estebank added the P-medium Medium priority label May 29, 2019
@estebank estebank added D-confusing Diagnostics: Confusing error or lint that should be reworked. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 10, 2019
@AaronKutch
Copy link
Contributor Author

I found simpler one of these in the wild on the subreddit ask anything thread (I have simplified it here). The new user of Rust is trying to set up Gtk signal handlers. What happens here is that the closure tries to use self which conflicts with the static lifetime. The problem is that the error message does not point to self in the dbg!(self.current_note); line that could be hidden in an arbitrary amount of code.
It also has that weird expected &mut Gui found &mut Gui line.

pub struct EventHandlingStruct;

impl EventHandlingStruct {
	fn connect_changed<F: Fn(&Self) + 'static>(&self, f: F) {}
}

pub struct Gui {
	current_note: u8
}

impl Gui {
	fn connect_signals(&mut self, builder: &EventHandlingStruct) {
		builder.connect_changed(move |_| {

			// bunch of code
			
			// this is the problem line that extends the lifetime of the closure too much
			dbg!(self.current_note);

			// bunch of code
		});
	}
}

produces this error on 1.44 nightly

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src\main.rs:13:27
   |
13 |           builder.connect_changed(move |_| {
   |  _________________________________^
14 | |
15 | |             // bunch of code
16 | |
...  |
20 | |             // bunch of code
21 | |         });
   | |_________^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 12:2...
  --> src\main.rs:12:2
   |
12 |       fn connect_signals(&mut self, builder: &EventHandlingStruct) {
   |  _____^
13 | |         builder.connect_changed(move |_| {
14 | |
15 | |             // bunch of code
...  |
21 | |         });
22 | |     }
   | |_____^
note: ...so that the types are compatible
  --> src\main.rs:13:27
   |
13 |           builder.connect_changed(move |_| {
   |  _________________________________^
14 | |
15 | |             // bunch of code
16 | |
...  |
20 | |             // bunch of code
21 | |         });
   | |_________^
   = note: expected  `&mut Gui`
              found  `&mut Gui`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@src\main.rs:13:27: 21:4 self:&mut Gui]` will meet its required lifetime bounds
  --> src\main.rs:13:11
   |
13 |         builder.connect_changed(move |_| {
   |                 ^^^^^^^^^^^^^^^

error: aborting due to previous error

Maybe the the variable that has the longest lifetime should be pointed out by the error.

@estebank estebank added the A-lifetimes Area: Lifetimes / regions label Apr 10, 2020
@crlf0710 crlf0710 added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jun 11, 2020
@AaronKutch
Copy link
Contributor Author

It looks like this was recently fixed on stable. The error for the first example is now:

error[E0621]: explicit lifetime required in the type of `string`
  --> src\lib.rs:43:22
   |
36 | pub fn count_words_parallel(string: &str, threads: usize)
   |                                     ---- help: add explicit lifetime `'static` to the type of `string`: `&'static str`
...
43 |         handles.push(thread::spawn(|| {
   |                      ^^^^^^^^^^^^^ lifetime `'static` required

and for the second is

error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
  --> src\lib.rs:40:27
   |
39 |       fn connect_signals(&mut self, builder: &EventHandlingStruct) {
   |                          --------- this data with an anonymous lifetime `'_`...
40 |           builder.connect_changed(move |_| {
   |  _________________________________^
41 | |
42 | |             // bunch of code
43 | |
...  |
47 | |             // bunch of code
48 | |         });
   | |_________^ ...is captured here...
   |
note: ...and is required to live as long as `'static` here
  --> src\lib.rs:40:11
   |
40 |         builder.connect_changed(move |_| {
   |                 ^^^^^^^^^^^^^^^

Fixing the errors and following more compiler errors appropriately fixes the problem. The only other problem I see is that it does not point out which line extends the lifetime, but I don't think it would be fixable in general. The error is much cleaner now.

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 A-lifetimes Area: Lifetimes / regions C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. P-medium Medium priority 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

4 participants