Borrowed value does not live long enough problem in for

Hi everyone! I have the problem with the next snippet. I am trying to read a line from a text file and count how many times the words are repeated.

use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::io::BufRead;
use std::path::Path;

fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
    where P: AsRef<Path>, {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

fn main(){
    println!("Contador de palabras");

    let lines = read_lines("./palabras.txt");

    match lines {
        Ok(lines) => {
            let  mut words_matches: HashMap<&str, i32> = HashMap::new();
            for line in lines {

                if let Ok(text) = line {
                    let lower_case_text: String = text.to_lowercase();
                    let words = lower_case_text.split(" ");
                    for word in words {
                        //https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.entry
                        let matches = words_matches.entry(word).or_insert(0);
                        *matches += 1;
                    }
                    println!("{:?}", words_matches);
                }
            }
        },
        Err(err) => println!("Error, {}", err)
    }

}

The compiler throw the next error:

                 let words = lower_case_text.split(" ");
   |                                 ^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
28 |                         let matches = words_matches.entry(word).or_insert(0);
   |                                       ------------- borrow later used here
...
32 |                 }
   |                 - `lower_case_text` dropped here while still borrowed

I'm confused about the borrowing here. I don't see how freeing lower_case_text is a problem in the hashmap.

Thanks!

Your HashMap takes its keys by reference, instead of owning it, but on every new line iteration the previos references are dropped. If you use String instead of &str, that would be fixed.

1 Like

Everything inside this block uses a temporary String which is dropped at the end of the block. words is an iterator over the String, and so each word is a borrowed &str with the lifetime of lower_case_text. These word references cannot outlive the if let block, which means you cannot use them as keys in the hash map.

The easiest fix is to have words_matches own its keys. Without borrows there are no lifetimes, and thus no lifetime issues.

let mut words_matches: HashMap<String, i32> = HashMap::new();
for word in words {
    words_matches.entry(word.to_owned())
        .and_modify(|m| { *m += 1 })
        .or_insert(0);
}

Awesome!! thank you for your time, it works!!!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.