I am a Rust noob with a medium level knowledge of programming in general and have been stuck in this spot for quite a while. Essentially, I am reading from a file and performing regex to break up each line into groups that I want to process later. I am hoping to save each line in a hashmap as a vector of its groups, with the key of the hashmap being group 4.
I want to use a String
to allow me to assign a value inside the for loop, but the captures_iter
function is expecting a &str
.
I am open to alternative solutions.
I alternate between two errors, and do not know a way around one without causing the other.
use std::fs::File;
use std::io::{BufRead, BufReader};
use regex::Regex;
use regex::Captures;
use std::collections::HashMap;
fn main() {
//read file
let input = File::open("../input.txt").unwrap();
let reader = BufReader::new(input);
//ugly regex, but it does exactly what I want it to
let re = Regex::new(r"(?:(^[[:lower:]] |^\d ) |)(?:([[:lower:]] |[[:upper:]] ) |)(?:([[:lower:]] |[[:upper:]] |\d |) |)-> ([[:lower:]] )$").unwrap();
//hashmap to store the 'instructions' which is the regex groups, with the key of group 4
let mut instructions : HashMap<&str, Vec<&str>> = HashMap::new();
for line in reader.lines().into_iter() {
let line = line.unwrap();
println!("Line Read: {}", line);
for caps in re.captures_iter(&line){ //this line is where I am having touble, with or without the '&' before line, I get a different error
let groups: Vec<&str> = caps.iter()
.map(|m| match m {
Some(value) => value.as_str(),
None => ""
})
.collect();
println!("{:?}", groups); //this prints exactly what I want
instructions.insert(groups[4], groups); //this line is the one that causes the error, because of a reference outside the loop?
}
}
}
The error when line:
for caps in re.captures_iter(&line){
isline
does not live long enough
The error when line:
for caps in re.captures_iter(line){
is mismatched types
I appreciate any help or advice. Please let me know if I can provide any more information.
P.S. This is for Advent of Code 2015, day 7. If you think I am going about this the complete wrong way also feel free to let me know.
CodePudding user response:
You can make the groups
to be a Vec<String>
instead of Vec<&str>
, and instructions
to be HashMap<String, Vec<String>>
, then it should work I think.
use regex::Captures;
use regex::Regex;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
//read file
let input = File::open("../input.txt").unwrap();
let reader = BufReader::new(input);
//ugly regex, but it does exactly what I want it to
let re = Regex::new(r"(?:(^[[:lower:]] |^\d ) |)(?:([[:lower:]] |[[:upper:]] ) |)(?:([[:lower:]] |[[:upper:]] |\d |) |)-> ([[:lower:]] )$").unwrap();
//hashmap to store the 'instructions' which is the regex groups, with the key of group 4
let mut instructions: HashMap<String, Vec<String>> = HashMap::new();
for line in reader.lines().into_iter() {
let line = line.unwrap();
println!("Line Read: {}", line);
for caps in re.captures_iter(&line) {
//this line is where I am having touble, with or without the '&' before line, I get a different error
let groups: Vec<String> = caps
.iter()
.map(|m| match m {
Some(value) => value.as_str().to_string(),
None => "".to_string(),
})
.collect();
println!("{:?}", groups); //this prints exactly what I want
instructions.insert(groups[4].clone(), groups); //this line is the one that causes the error, because of a reference outside the loop?
}
}
}
CodePudding user response:
The values you store in your hashmap go out of scope at the end of each loop iteration, which is why Rust complains about lifetimes.
// A new `line` is created on each loop iteration.
for line in reader.lines.into_iter() {
let line = line.unwrap();
for caps in re.captures_iter(&line) {
// This `Vec` stores slices of `line`.
let groups: Vec<&str> = /* ... */;
// Here you insert values into the hashmap that reference `line`.
instructions.insert(groups[4], groups);
}
// `line` goes out of scope here, invalidating the stored references to it.
}
In order to avoid the error you see, you need to store the data in the HashMap
such that it outlives each loop iteration. The simplest way to do this is to make it a HashMap<String, Vec<String>>
and convert the &str
s to Strings
when storing them. That will copy the string data from line
to a separate location on the heap, which will live as long as each stored string.
// A new `line` is created on each loop iteration.
for line in reader.lines.into_iter() {
let line = line.unwrap();
for caps in re.captures_iter(&line) {
// This `Vec` stores owned copies of the data in `line`.
let groups: Vec<String> = caps
.iter()
.map(|m| match m {
Some(value) => value.as_str().to_string(),
None => String::new(),
})
.collect();
// All values inserted here are owned.
instructions.insert(groups[4].clone(), groups);
}
// There are no outstanding references to `line`.
}
I recommend checking out The Rust Programming Language chapters on References and Borrowing and Validating References with Lifetimes to gain a better understanding of the error you encountered.