Storing from within a loop a borrowed value into a container in outer space?
I set myself a small challenge to get some basic knowledge of Rust. A task:
Read a few key-value pairs from stdin and put them into a hashmap.
This, however, turned out to be more difficult than expected. Mainly because of understanding the timing of life. The following code is what I currently have after some experimentation, but the compiler just doesn't stop yelling at me.
use std::io;
use std::collections::HashMap;
fn main() {
let mut input = io::stdin();
let mut lock = input.lock();
let mut lines_iter = lock.lines();
let mut map = HashMap::new();
for line in lines_iter {
let text = line.ok().unwrap();
let kv_pair: Vec<&str> = text.words().take(2).collect();
map.insert(kv_pair[0], kv_pair[1]);
}
println!("{}", map.len());
}
The compiler basically says:
`text` does not live long enough
As far as I understand, this is due to the fact that the lifetime of the "text" is limited by the volume of the cycle. Therefore, the key-value pair that I retrieve in the loop is also bound to the boundaries of the loops. So inserting them into an external map will result in a dangling pointer since after each iteration the "text" will be destroyed. (Please tell me if I'm wrong)
The big question is, how do you solve this problem?
My intuition says:
Make an "owned copy" of the key value pair and "expand" its lifetime to the outer scope ... but I have no idea how to achieve this.
source to share
The lifetime of the "text" is limited by the volume of the cycle. Therefore, the key-value pair that I retrieve in the loop is also bound to the boundaries of the loops. So inserting them into an external map will result in a dangling pointer since after each iteration the "text" will be destroyed.
Sounds right at me.
Make an "owned copy" of the key value pair.
The property &str
has String
:
map.insert(kv_pair[0].to_string(), kv_pair[1].to_string());
Edit
The original code is below, but I've updated the answer above to be more idiomatic
map.insert(String::from_str(kv_pair[0]), String::from_str(kv_pair[1]));
source to share
In Rust, the1.1
feature has words
been deprecated. You should now be using split_whitespace
.
Here's an alternative solution that is a little more functional and idiomatic (works with 1.3
).
use std::io::{self, BufRead};
use std::collections::HashMap;
fn main() {
let stdin = io::stdin();
// iterate over all lines, "change" the lines and collect into `HashMap`
let map: HashMap<_, _> = stdin.lock().lines().filter_map(|line_res| {
// convert `Result` to `Option` and map the `Some`-value to a pair of
// `String`s
line_res.ok().map(|line| {
let kv: Vec<_> = line.split_whitespace().take(2).collect();
(kv[0].to_owned(), kv[1].to_owned())
})
}).collect();
println!("{}", map.len());
}
source to share