Advent of Code 2024, Day 1
I am doing Advent of Code 2024 in Rust! This is day 1, chronicles on learnings and solutions.
Part of the AoC 2024 series
- Day 1 post (You are here! š)
- Day 2 post
New year, new me, new Advent of Code! This year Iām using Rust to solve these, because I now have enough handle on Golang that the weakest link is the knowledge, or rather lack thereof, of advanced data manipulation algorithms rather than the language itself.
Rust however, Iām not so great at, so AoC is my way of getting my katas in.
This is going to be a series of blog posts, this one being the one, and whichever I get stuck on is going to be the last in the series. They will all be linked in some fancypants dropdown list someplace. I havenāt figured that part out yet.
As usual, spoilers be here, as Iām going to write about the solutions, and the challenges I faced.
Oh, links, if you want to follow along:
Day 1
All right, this is basically parsing a file into two different lists, sorting them, and then comparing them side by side.
Reading the file was fairly easy, I didnāt get stuck on it much, besides not really being 100% sure if I use a workspace, what fs::read_to_string()
is going to consider the ācurrentā directory, from which we need ito be relative to where the file is. Turns out itās relative to the project root, rather than the workspace member root.
The second hurdle was figuring out this piece of code:
let boo: Vec<i32> = trimmed
.split("\n")
.flat_map(|line: &str| -> Vec<i32> {
line.split(" ")
.map(|x: &str| -> i32 { x.parse::<i32>().unwrap_or(i32::MAX) })
.collect::<Vec<i32>>()
})
.collect();
trimmed
is the entire file with the trailing newline removed. The amount of āyou have to specify the type in the closure!!ā and collect
and iter
and into_iter
issues Iāve run into is huge. I still kind of donāt understand how and why flat_map
works, but Iām going to trust the process and the later days.
Break the file contents by newline, then for each line break it by three spaces, then for those parts try to convert them to i32
sizes, return the (2 long) vector of the two numbers, and then flatten the vector of vectors.
And then iterate over it, and put the numbers left and right based on the index being divisible by 2, and then sort them.
let mut left_list: Vec<i32> = Vec::new();
let mut right_list: Vec<i32> = Vec::new();
for (i, x) in boo.into_iter().enumerate() {
if i % 2 == 0 {
left_list.push(x);
} else {
right_list.push(x);
}
}
left_list.sort();
right_list.sort();
But I canāt sort them in place, or rather I canāt use the .sort()
method when Iām also assigning them to a struct, because ugh. So this wonāt work:
Day01 {
list_left: left_list.sort(),
list_right: right_list.sort(),
}
I just want to assign or move the sorted lists. No, the expected type of Vec<i32>
, what list_left
and list_right
are, arenāt of type ()
. Super helpful. No, I have to sort them first, and then assign them, like I do in the source code.
Actually summing differences is rather trivial.
Part 2
This introduces a hashmap. This thing: https://doc.rust-lang.org/std/collections/struct.HashMap.html. Super easy. Iām also using RustRover as my IDE, and that has code intelligence based on local code, so when I wrote a for loop for my right list, and I started with map
, it being a hashmap, it knew that I probably want to create a frequency table, and autocompleted the rest of the line for me:
for x in self.list_right.iter() {
map.entry(x).and_modify(|e| *e += 1).or_insert(1);
}
Am I cheating with this? I donāt feel like I am, because itās not doing anything I donāt understand, but it did help me figure out that I have the .entry
method, and the .and_modify
method, and the .or_insert
method. All of which are on the intro to hash maps page as above.
The remaining issue was me being dumb and not reading the exercise correctly, and summing the wrong thing.
Two āļøāļø later, itās time to go to sleep.
Until tomorrow, friends!