Reputation: 3226
Trying to complete the "Hash Maps" chapter of the Rust book at https://doc.rust-lang.org/book/2018-edition/ch08-03-hash-maps.html , with this code:
extern crate regex;
use std::collections::HashMap;
use std::io;
use regex::Regex;
fn get_command() -> String {
let mut input_cmd = String::new();
io::stdin().read_line(&mut input_cmd)
.expect("Failed to read command");
let input_cmd = input_cmd.trim();
input_cmd.to_string()
}
fn main() {
println!("Add someone by typing e.g. \"Add Sally to Engineering\", list everyone in a department by typing e.g. \"List everyone in Sales\", or list everyone by typing \"List everyone\". To quit, type \"Quit\".");
let mut employees_by_dept: HashMap<&str, Vec<&str>> = HashMap::new();
let add_to_dept_re = Regex::new("^Add ([A-Za-z]+) to ([A-Za-z]+)$").unwrap();
let list_in_dept_re = Regex::new("^List everyone in ([A-Za-z]+)$").unwrap();
let list_all_re = Regex::new("^List everyone$").unwrap();
loop {
let input_cmd = get_command();
let caps = add_to_dept_re.captures(&input_cmd).unwrap();
if add_to_dept_re.is_match(&input_cmd) {
let dept_name = caps.get(2).unwrap().as_str();
let employee_name = caps.get(1).unwrap().as_str();
println!("Adding person");
employees_by_dept.entry(&dept_name)
.or_insert_with(Vec::new)
.push(employee_name);
} else if list_in_dept_re.is_match(&input_cmd) {
println!("Listing people");
} else if list_all_re.is_match(&input_cmd) {
println!("Listing everyone");
} else if input_cmd == "Quit" {
break;
} else {
println!("Invalid command");
break;
}
}
println!("Bye!");
}
But I get this:
error[E0597]: `input_cmd` does not live long enough
--> src/main.rs:28:45
|
28 | let caps = add_to_dept_re.captures(&input_cmd).unwrap();
| ^^^^^^^^^ borrowed value does not live long enough
...
48 | }
| - `input_cmd` dropped here while still borrowed
...
51 | }
| - borrowed value needs to live until here
Have tried .captures(&input_cmd.clone())
and various other things, but doesn't help. Any ideas?
Upvotes: 0
Views: 323
Reputation: 18943
Rust memory safety rules prevents this type of approach: your HashMap value outlives the inserted items.
See embedded comments below but especially the Ownership chapter of the book.
fn main() {
let mut employees_by_dept: HashMap<&str, Vec<&str>> = HashMap::new();
let add_to_dept_re = Regex::new("^Add ([A-Za-z]+) to ([A-Za-z]+)$").unwrap();
let list_in_dept_re = Regex::new("^List everyone in ([A-Za-z]+)$").unwrap();
let list_all_re = Regex::new("^List everyone$").unwrap();
loop {
let input_cmd = get_command();
let caps = add_to_dept_re.captures(&input_cmd).unwrap();// <--- input_cmd
//is borrowed here
// ... code for getting dept_name and employee_name references
// and inserting into HashMap omitted
} // <----- The String input_cmd is dropped here (memory is freed)
// this implies that dept_name and employee_name references
// points to deallocated memory
// ... At this point you will have a live employees_by_dept HashMap
// that contains references to deallocated memory
println!("Bye!");
}
Make instead the HashMap take ownership of the keys/items values:
fn main() {
println!("Add someone by typing e.g. \"Add Sally to Engineering\", list everyone in a department by typing e.g. \"List everyone in Sales\", or list everyone by typing \"List everyone\". To quit, type \"Quit\".");
let mut employees_by_dept: HashMap<String, Vec<String>> = HashMap::new();
let add_to_dept_re = Regex::new("^Add ([A-Za-z]+) to ([A-Za-z]+)$").unwrap();
let list_in_dept_re = Regex::new("^List everyone in ([A-Za-z]+)$").unwrap();
let list_all_re = Regex::new("^List everyone$").unwrap();
loop {
let input_cmd = get_command();
let caps = add_to_dept_re.captures(&input_cmd).unwrap();
if add_to_dept_re.is_match(&input_cmd) {
let dept_name = caps.get(2).unwrap().as_str();
let employee_name = caps.get(1).unwrap().as_str();
println!("Adding person");
employees_by_dept
.entry(dept_name.to_string())
.or_insert_with(Vec::new)
.push(employee_name.to_string());
} else if list_in_dept_re.is_match(&input_cmd) {
println!("Listing people");
} else if list_all_re.is_match(&input_cmd) {
println!("Listing everyone");
} else if input_cmd == "Quit" {
break;
} else {
println!("Invalid command");
break;
}
}
println!("Bye!");
}
Upvotes: 4