Reputation: 92
I'm currently trying to solve this challenge to learn Rust. I've created my own solution below:
use std::io;
use std::vec::Vec;
use std::collections::HashSet;
fn main() {
let stdin = io::stdin();
let mut buffer = String::new();
let mut set = HashSet::new();
let req = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid", "cid"];
let mut res: u32 = 0;
while stdin.read_line(&mut buffer).unwrap() > 0 {
if buffer == "\n" {
for r in req.iter() {
if set.contains(r) {
set.remove(r);
}
}
if (set.len() == req.len()) || (set.len() == 7 && !set.contains("cid")) {
res += 1;
}
} else {
if buffer.ends_with('\n') {
buffer.pop();
}
let inputs: Vec<&str> = buffer.split_whitespace().collect();
for key_val in inputs {
let kval: Vec<&str> = key_val.split(':').collect();
set.insert(kval[0]);
}
}
buffer.clear();
}
println!("{}", res);
}
When I tried to compile it, the compiler outputs the error:
error[E0502]: cannot borrow `buffer` as mutable because it is also borrowed as immutable
if (set.len() == req.len()) || (set.len() == 7 && !set.contains("cid")) {
| --- immutable borrow later used here
...
28 | let inputs: Vec<&str> = buffer.split_whitespace().collect();
| ------ immutable borrow occurs here
...
37 | buffer.clear();
| ^^^^^^^^^^^^^^ mutable borrow occurs here
I've solved other problems by using this kind of style before (using &mut buffer
and split
it somewhere) without getting this error.
Why does this error occur here and how can I fix it?
Upvotes: 1
Views: 1122
Reputation: 98338
Look at the type of your set
variable. You can get it by writing a set as ()
just after the set.insert(...)
line. You'll see this error message:
non-primitive cast: `HashSet<&str>` as `()`
So you set holds references to str
. But where do these references live. Well, all your str
ultimately come from buffer
so they are references to the contents of buffer
. If you were allowed to do buffer.clear()
all these references would become invalidated.
You have two basic options:
set
are not invalidated. You could use `readString
instead of &str
values in the set
.The easier solution is #2:
set.insert(kval[0].to_owned());
With that and a few other minor fixes, your algorithm will compile (playground).
Option #1 would require to read the whole stdin into a variable and then iterate over the lines:
use std::io::Read;
let mut stdin = io::stdin();
let mut input = String::new();
stdin.read_to_string(&mut input).unwrap();
for buffer in input.lines() {
...
}
Now buffer
is a &str
instead of a String
, but the lifetime of the values in set
will be that of input
that is outside of the loop. You would need a few minor fixes, particularly with the end-of-line checks.
Upvotes: 3