Yeah, lots of room for improvement; in no particular order:
-
return
at the end of functions is unnecessary. Rust is an expression language.
- Lots of mutation and looping through collections could be replaced by iterator chains and
collect()
or the vec![]
macro, etc.
-
&Vec<_>
as a function argument is harmful. You should pass &[_]
instead. A lot of types coerce to a slice, but not many coerce to a vector. Therefore, unless you already have a vector to begin with, you'll force the caller to allocate a Vec
just for the sake of being able to pass a Vec
.
- The same applies to
&String
vs. &str
, too.
-
(&s[..middle], &s[middle..])
is in std under the name str::split_at()
(same for slices).
- Using
enumerate()
and then explicitly indexing into the slice being iterated over seems weird. Use .chunks()
instead.
- You are using
String
all over the place. In many cases, a char
would suffice.
- You are cloning a
u32
in get_priority()
. That's useless.
- You are using
u32
for counting. Don't do that. Use usize
instead.
Overall, this is the cleaned up code, with the following modifications (~20% shorter):
diff --git a/src/day03.rs b/src/day03.rs
index 46561ca..74b733b 100644
--- a/src/day03.rs
+++ b/src/day03.rs
@@ -2,147 +2,116 @@ use crate::utils::load_from_file;
use std::collections::BTreeMap;
struct Racksuck {
- priority: u32,
+ priority: usize,
}
// https://adventofcode.com/2022/day/3
pub fn solve_day_03() {
println!("--- Day 3: Rucksack Reorganization ---");
- let priorities_table: BTreeMap<String, u32> = build_priorities_table();
+ let priorities_table = build_priorities_table();
test_example(&priorities_table);
let racksucks = build_racksucks(&priorities_table, &get_input());
assert_eq!(racksucks.len(), 300);
println!(
" The sum of the priorities of all duplicate item types is {}",
- calculate_priority_sum(racksucks)
+ calculate_priority_sum(&racksucks)
);
println!(" --- Part Two ---");
test_example_p2(&priorities_table);
let racksucks = build_racksucks_p2(&priorities_table, &get_input());
println!(
" The sum of the priorities of those types is {}",
- calculate_priority_sum(racksucks)
+ calculate_priority_sum(&racksucks)
);
}
-fn test_example(priorities_table: &BTreeMap<String, u32>) {
+fn test_example(priorities_table: &BTreeMap<char, usize>) {
let racksucks = build_racksucks(&priorities_table, &get_example_input());
- assert_eq!(calculate_priority_sum(racksucks), 157);
+ assert_eq!(calculate_priority_sum(&racksucks), 157);
}
-fn test_example_p2(priorities_table: &BTreeMap<String, u32>) {
+fn test_example_p2(priorities_table: &BTreeMap<char, usize>) {
let racksucks = build_racksucks_p2(&priorities_table, &get_example_input());
- assert_eq!(calculate_priority_sum(racksucks), 70);
+ assert_eq!(calculate_priority_sum(&racksucks), 70);
}
-fn get_priority(priorities_table: &BTreeMap<String, u32>, duplicate_item_type: &String) -> u32 {
- let priority: u32 = match priorities_table.get(duplicate_item_type) {
- Some(&priority) => priority,
- _ => 0,
- };
- return priority.clone();
+fn get_priority(priorities_table: &BTreeMap<char, usize>, duplicate_item_type: char) -> usize {
+ priorities_table.get(&duplicate_item_type).copied().unwrap_or(0)
}
-fn build_priorities_table() -> BTreeMap<String, u32> {
- let mut table: BTreeMap<String, u32> = BTreeMap::new();
- let mut counter = 0;
- for chr in 'a'..='z' {
- counter += 1;
- table.insert(chr.to_string(), counter);
- }
- for chr in 'A'..='Z' {
- counter += 1;
- table.insert(chr.to_string(), counter);
- }
- return table;
+fn build_priorities_table() -> BTreeMap<char, usize> {
+ ('a'..='z')
+ .chain('A'..='Z')
+ .enumerate()
+ .map(|(i, c)| (c, i + 1))
+ .collect()
}
fn get_example_input() -> Vec<String> {
- let mut result: Vec<String> = Vec::new();
- result.push(String::from("vJrwpWtwJgWrhcsFMMfFFhFp"));
- result.push(String::from("jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL"));
- result.push(String::from("PmmdzqPrVvPwwTWBwg"));
- result.push(String::from("wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn"));
- result.push(String::from("ttgJtRGJQctTZtZT"));
- result.push(String::from("CrZsJsPPZsGzwwsLwLmpwMDw"));
- return result;
+ vec![
+ String::from("vJrwpWtwJgWrhcsFMMfFFhFp"),
+ String::from("jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL"),
+ String::from("PmmdzqPrVvPwwTWBwg"),
+ String::from("wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn"),
+ String::from("ttgJtRGJQctTZtZT"),
+ String::from("CrZsJsPPZsGzwwsLwLmpwMDw"),
+ ]
}
fn get_input() -> Vec<String> {
- return load_from_file("src/puzzle_inputs/day03.txt");
+ load_from_file("src/puzzle_inputs/day03.txt")
}
-fn build_racksucks(priorities_table: &BTreeMap<String, u32>, input: &Vec<String>) -> Vec<Racksuck> {
- let mut result: Vec<Racksuck> = Vec::new();
- for line in input {
- let length = line.len();
- let start = 0;
- let middle = length / 2;
- let end = length;
-
- let compartment_one: String = line[start..middle].to_string();
- let compartment_two: String = line[middle..end].to_string();
- result.push(build_racksuck(
- priorities_table,
- &compartment_one,
- &compartment_two,
- ));
- }
- return result;
+fn build_racksucks(priorities_table: &BTreeMap<char, usize>, input: &[String]) -> Vec<Racksuck> {
+ input
+ .iter()
+ .map(|line| {
+ let (head, tail) = line.split_at(line.len() / 2);
+ build_racksuck(priorities_table, head, tail)
+ })
+ .collect()
}
fn build_racksucks_p2(
- priorities_table: &BTreeMap<String, u32>,
- input: &Vec<String>,
+ priorities_table: &BTreeMap<char, usize>,
+ input: &[String],
) -> Vec<Racksuck> {
- let mut result: Vec<Racksuck> = Vec::new();
- for (pos, line) in input.iter().enumerate().step_by(3) {
- let group_line1 = line;
- let group_line2 = &input[pos + 1];
- let group_line3 = &input[pos + 2];
-
- for i in group_line1.chars() {
- if group_line2.contains(i) && group_line3.contains(i) {
- result.push(build_racksuck_p2(priorities_table, &i.to_string()));
- break;
- }
- }
- }
- return result;
+ input
+ .chunks(3)
+ .filter_map(|lines| {
+ let [group_line1, group_line2, group_line3] = <&[_; 3]>::try_from(lines).unwrap();
+
+ group_line1
+ .chars()
+ .find(|&c| group_line2.contains(c) && group_line3.contains(c))
+ .map(|c| build_racksuck_p2(priorities_table, c))
+ })
+ .collect()
}
fn build_racksuck(
- priorities_table: &BTreeMap<String, u32>,
- compartment_one: &String,
- compartment_two: &String,
+ priorities_table: &BTreeMap<char, usize>,
+ compartment_one: &str,
+ compartment_two: &str,
) -> Racksuck {
- let duplicate_item_type: String = get_duplicate_item_type(&compartment_one, &compartment_two);
- let priority = get_priority(priorities_table, &duplicate_item_type);
+ let priority = match get_duplicate_item_type(&compartment_one, &compartment_two) {
+ Some(duplicate_item_type) => {
+ get_priority(priorities_table, duplicate_item_type)
+ }
+ None => 0
+ };
Racksuck { priority }
}
-fn build_racksuck_p2(priorities_table: &BTreeMap<String, u32>, badge: &String) -> Racksuck {
- let priority = get_priority(priorities_table, &badge);
+fn build_racksuck_p2(priorities_table: &BTreeMap<char, usize>, badge: char) -> Racksuck {
+ let priority = get_priority(priorities_table, badge);
Racksuck { priority }
}
-fn get_duplicate_item_type(compartment_one: &String, compartment_two: &String) -> String {
- let mut result: String = String::new();
- for i in compartment_one.chars() {
- for y in compartment_two.chars() {
- if i == y {
- result = i.to_string();
- break;
- }
- }
- }
- return result;
+fn get_duplicate_item_type(compartment_one: &str, compartment_two: &str) -> Option<char> {
+ compartment_one.chars().find(|&c| compartment_two.contains(c))
}
-fn calculate_priority_sum(racksucks: Vec<Racksuck>) -> u32 {
- let mut result: u32 = 0;
- for racksuck in racksucks {
- result += racksuck.priority;
- }
- return result;
+fn calculate_priority_sum(racksucks: &[Racksuck]) -> usize {
+ racksucks.iter().map(|r| r.priority).sum()
}