My solutions to Advent of Code 2018, which I'm doing in Rust (again).
I've been playing with Rust on and off for about a year, mostly off. I'm definitely still a beginner, and overall my programming logic is... spotty and informal. In general I strive for readability over brevity, and I rarely use exotic methods when a simpler one will do (even if it takes a few more lines).
(I used Rust for the 2017 AoC, too, but didn't get super far. I'm hoping to get 20 stars (out of a possible 50) by Christmas this year.)
Each day's challenge (1 through, hopefully-but-probably-not-all-the-way-to, 25) is a Rust executable in src/bin
. Thus the code for, say, Day 2's executable is located in src/bin/day02.rs
. To run the Day 2 executable, from the root directory run cargo run --bin day02
. To run tests, if there are any, run cargo test --bin day02
.
The input for each challenge is located in inputs
and named by the day (so for example, inputs/day02.txt
).
- 8 (or So) Lessons from Days 1 and 2
- Optimizing Rust: The Evolution of My Day 5 Advent of Code Solution
Other folks posting their Rust solutions to GitHub. Plenty more discussion on r/adventofcode.
A lot of the Advent of Code puzzles require similar io tasks. Here are some functions I use often, sometimes tweaking them. I may throw some or all of them into a file in src/lib
and import them as needed, but for now I've just been copy-and-pasting them into the executables as I need them.
This one reads a multi-line text file into a Vector. The Vector will be whatever Rust type that Rust's parse
function gets from the file.
// from https://github.com/sts10/eyeoh/blob/master/src/lib.rs#L33 use std::fs::File; use std::io; use std::io::BufRead; use std::io::BufReader; use std::str::FromStr; fn read_by_line<T: FromStr>(file_path: &str) -> io::Result<Vec<T>> { let mut vec = Vec::new(); let f = match File::open(file_path.trim_matches(|c| c == '\'' || c == ' ')) { Ok(res) => res, Err(e) => return Err(e), }; let file = BufReader::new(&f); for line in file.lines() { match line?.parse() { Ok(l) => vec.push(l), Err(_e) => { panic!("Error reading a line of the file"); } } } Ok(vec) }
If the input file is only one line with tons of characters we're going to need to iterate through, this function is handy. It reads a text file into a Vector of char
s (characters), which is usually what we want when doing AoC challenges.
use std::fs::File; use std::io; use std::io::prelude::*; fn read_string_from_file_to_vector(file_path: &str) -> io::Result<Vec<char>> { let mut f = match File::open(file_path.trim_matches(|c| c == '\'' || c == ' ')) { Ok(res) => res, Err(e) => return Err(e), }; let mut string_from_file = String::new(); f.read_to_string(&mut string_from_file) .expect("something went wrong reading the file"); let mut vector_of_chars = Vec::new(); for c in string_from_file.chars() { vector_of_chars.push(c); } Ok(vector_of_chars) }
I found myself often wanting to split a string slice (&str
) by another string slice and get a vector back.
fn split_and_vectorize<'a>(string_to_split: &'a str, splitter: &str) -> Vec<&'a str> { let split = string_to_split.split(splitter); split.collect::<Vec<&str>>() }
Usage:
let event_string: &str = "Guard 13 fell asleep at 7:56"; let guard_id: usize = split_and_vectorize(event_string, " ")[1].parse().unwrap(); println!("guard id is {}", guard_id); // can nest calls to dial in a bit more let hour: u32 = split_and_vectorize(split_and_vectorize(event_string, " ")[5], ":")[0].parse().unwrap(); println!("At hour {}, guard #{} fell asleep", hour, guard_id); // Take multiple elements from a split with one call (but 2 lines, including the join) let phrase = &split_and_vectorize(event_string, " ")[2..=3]; let collected_phrase = phrase.join(" "); println!("collected phrase is {}", collected_phrase);
To do: have the splitter be a vector of splitters so you found just do split_and_vectorize(event_string, vec![" ", ":"])[5]
to get hours
.