I am counting the number of times a word appears in Macbeth:
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::collections::HashMap;
fn main() {
let f = File::open("macbeth.txt").unwrap();
let reader = BufReader::new(f);
let mut counts = HashMap::new();
for l in reader.lines() {
for w in l.unwrap().split_whitespace() {
let count = counts.entry(w).or_insert(0);
*count += 1;
}
}
println!("{:?}", counts);
}
Rust barfs on this, saying:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:14:9
|
11 | for w in l.unwrap().split_whitespace() {
| ---------- temporary value created here
...
14 | }
| ^ temporary value dropped here while still borrowed
...
18 | }
| - temporary value needs to live until here
|
= note: consider using a `let` binding to increase its lifetime
The actual problem is that w
is a reference, and so changing it to w.to_string()
solves it. I don't get why the Rust compiler is pointing the blame at l
, when the issue is w
. How am I supposed to infer that w
is the problem here?
The error is both right and wrong here.
l
is blamed, becausew
lives only as long asl
(andl.unwrap()
) andl
doesn't live long enough to put it in hashmap in a higher scope.In practice, you just have to look at what other variables depend on the lifetime of a variable the compiler complains about.
But Rust is also working lately on improving error reporting, so I'd raise this case as potential bug.
It's not, really. Review the error message again:
The error marker is pointing to the call of
unwrap
onl
.It's not, really.
l
is of typeResult<String>
. When you callunwrap
, you get aString
, and thensplit_whitespace
returns references to that string. These references live only as long as the string, but your code tries to put them into a hashmap that will live longer than the string. The problem is that thel.unwrap()
doesn't live long enough, andw
is just a reference to the thing that doesn't live long enough.Conceptually, it's the same problem as this code:
Which also points to
s
and says it doesn't live long enough (because it doesn't).The correct solution is to convert each word into an owned
String
which theHashMap
can then hold: