Confused about lifetime of hashmap key

I have some codes here

extern crate regex;

use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, BufReader};
use regex::Regex;

fn main () {
    let filename:&str = "/etc/hosts";
    let file = File::open(filename);
    match file {
        Ok(f) => {
            println!("[DEBUG] {} opened.", filename);
            foo(f);
        }
        Err(e) => {
            println!("[DEBUG] {}", e);
        }
    }
}

fn foo (f: File) {
    // I have a mutable HashMap
    let mut hash_map: HashMap<&str, u16> = [
        ("R0", 0),
        ("R1", 1),
        ("R2", 2),
        ("R3", 3)
    ].iter().cloned().collect();
    
    // I have a big string named 'all', is read from a text file
    let mut reader: BufReader<File> = BufReader::new(f);
    let mut all = String::new();
    reader.read_to_string(&mut all).unwrap();
    
    // And split it into lines, and remove the empty lines
    let lines: Vec<&str> = all.split('\n')
        .map(|line| line.trim())
        .filter(|line| line.len() > 0).collect();
        
    let reg = Regex::new(r"^((?:\d{1,3})(?:\.\d{1,3}){3})").unwrap();
    let mut current: u16 = 0;
    // Now, from these lines, I got some keys,
    // and want to insert into the hash_map
    lines.into_iter().for_each(|line| {
        if reg.is_match(line) {
            let key = &reg.captures(line).unwrap()[1];
            println!("[DEBUG] capture key: {}", key);
            // But, I got lifetime error here
            // and I had tried many ways to deal with lifetime,
            // But it doesn't work, so how to fix it?
            hash_map.insert(key, current);
        } else {
            current = current + 1;
        }
    });
    
    // println!("{:?}", lines);
    
    
    for (k, v) in hash_map {
        println!("{}: {}", k, v);
    }
}

How to fix it? I had tried many ways, not works.
And there's a plagyground link below,
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=228b677b77fc889b085631385cbdced3

Buried deep in the regex::Captures docs, we can find this statement about the indexing operator:

The text can't outlive the Captures object if this method is used, because of how Index is defined (normally a[i] is part of a and can't outlive it); to do that, use get() instead.

Following this advice, you can fix your code by replacing the problem line with:

let key = reg.captures(line).unwrap().get(1).unwrap().as_str();

(Playground)


EDIT: Another way to fix this is by storing String keys instead of &str keys. It might be a little less efficient due to the extra heap allocations, but means you don’t have to worry about lifetimes as much: The HashMap owns its keys outright instead of borrowing them, so you don’t need to separately keep the original data sopurce alove.

let mut hash_map: HashMap<String, u16> = [
    ("R0", 0),
    ("R1", 1),
    ("R2", 2),
    ("R3", 3)
].iter().map(|&(k,v)| (String::from(k), v)).collect();
hash_map.insert(String::from(key), current);

(Playground)

Well, that's a better solution than mine, I just tried change the key from &str to String and it works. Thanks for you quick response.

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.