Error about "does not live long enough" could be more helpful
When we receive a "(var) does not live long enough" error message, the compiler doesn't always tell you which line is actually causing the problem. For example (extracted from a larger program):
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io;
use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write};
fn main() {
let args: Vec<String> = env::args().collect();
match args.len() {
3 => {
let in_name = &args[1];
let out_name = &args[2];
assemble(in_name, out_name).unwrap();
}
_ => {
println!("Usage: hack-asm input_file output_file");
}
}
}
fn assemble(in_name: &str, out_name: &str) -> io::Result<()> {
let in_file = try!(File::open(in_name));
let out_file = try!(File::create(out_name));
let mut reader = BufReader::new(in_file);
let mut writer = BufWriter::new(out_file);
// First, build up all labels
let mut label_map = HashMap::new();
let mut instruction_address = 0;
for line in reader.by_ref().lines() {
let line = line.unwrap();
let mut line = line.trim();
// Strip any comments
if let Some(idx_comment) = line.find("//") {
line = &line[0..idx_comment].trim();
}
if line.is_empty() {
// Not an instruction, skip.
} else if line.starts_with('(') && line.ends_with(')') {
let label = &line[1..line.len() - 1];
if label.is_empty() {
panic!("Empty label");
} else if let Some(label) = label_map.insert(label, instruction_address) {
panic!("Label {:?} was already defined.", label);
}
} else {
// Assume it's an instruction. If it's not, validation will fail further below anyways.
instruction_address += 1;
}
}
Ok(())
}
Compiling this will give the following error:
error: `line` does not live long enough
--> main.rs:57:5
|
36 | let mut line = line.trim();
| ---- borrow occurs here
...
57 | }
| ^ `line` dropped here while still borrowed
...
60 | }
| - borrowed value needs to live until here
error: aborting due to previous error
Neither of the shown lines are that close to where the problem actually is, on line 50:
} else if let Some(label) = label_map.insert(label, instruction_address) {
Changing label to label.to_string() fixes the error and allows the borrow check to pass.
It would definitely be an improvement (particularly for beginners to the language) if the compiler could tell you which line is holding onto the value, preventing the borrow check from passing.
This would be huge for me. I usually waste about 5-10 minutes when writing Rust code staring at some code trying to determine where the problem is with this error message, desperately trying to comment and uncomment lines until I find the offending one. When I do find it it's obvious, but looking for it incurs a lot of guesswork.
I think the best would be to both :- Print the offending line where the value is held, as suggested above,
- Specify the types involved. Specifying them more often than not helps me understand the problem, and saves a round trip of adding spurious temporary variables and using () as a type just to get them printed and see what I'm dealing with.
orvly at 2018-03-18 19:43:49
-
error[E0597]: `line` does not live long enough --> src/main.rs:34:24 | 34 | let mut line = line.trim(); | ^^^^ borrowed value does not live long enough ... 48 | } else if let Some(label) = label_map.insert(label, instruction_address) { | --------- borrow later used here ... 55 | } | - `line` dropped here while still borrowedEsteban Kuber at 2019-04-28 01:05:22
Triage: no change besides pointing at the whole
insertexpression now. It might be nice to point atlabelin particular in a separate note, potentially suggesting to change it to be an owned value, but the original ask has been resolved. Mentioning the involved types might be one way of helping here, as suggested 5 years ago (!) by @orvly.Esteban Kuber at 2022-12-15 20:03:28