Error about "does not live long enough" could be more helpful

3209d3a
Opened by Learn OpenGL ES at 2022-12-15 20:03:28

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.

  1. 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 :

    1. Print the offending line where the value is held, as suggested above,
    2. 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

  2. Current output:

    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 borrowed
    

    Esteban Kuber at 2019-04-28 01:05:22

  3. Triage: no change besides pointing at the whole insert expression now. It might be nice to point at label in 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