Last Updated: 2022-12-07
Table of Contents
Note: Each challenge has a Real Talk section where I share some Takeaways about what I learned during that challenge and how it contributed to my overall goal of learning Rust, various Notes I wrote down while going through the challenge, all of the Searches I made while working through the challenge, and what my key Next Steps were so you can see how I broke down trying to learn these concepts by doing.
Anyone who has dealt with Python, Node, or Ruby versions knows that you probably shouldn't just install whatever language you wnat to work with without first researching the ecosystem and figuring out how people really manage versions, packages, etc. You can just install the default toolchain and go for it, but you'll eventually regret it.
As I was searching for the right package to install from Homebrew for this, I learned the first thing that I love about Rust.
Rust's default, recommended installation method,
rustup handles this for you so that you don't need to figure out whether you should be using
This instantly made the languages I have been using for years feel very outdated.
Below are some conclusions I've come to during this stage of my journey (along with the date that I came to that conclusion just in case my thoughts change over time).
- 2022-12-02: Rust is pretty neat. I might actually like it.
- 2022-12-04: Fighting to get the right types (like
- 2022-12-05: Like any language, it seems like it is not hard to write mediocre, naive implementations in Rust.
- 2022-12-06: Feeling a bit more comfortable.
- 2022-12-07: What a roller coaster of emotion.
Now that we've got rust up and running, it's time to see what @ericwastl has in store for us with Advent of Code this year.
Luckily Day One eases us into things with iterating through some collections of numbers and adding them together.
Note: I first actually needed to figure out how to get the
rust-analyzer VSCode Extension to cooperate with the subdirectory I used for my crate.
Another Note: I'll also probably streamline the input gathering at some point so that my inputs are pulled down from the Advent of Code site automatically and cached, but we need to walk before we can run and we're taking baby steps here.
We're given an example input:
And told that each collection of numbers represents some collection and we want to sum those collections and find the biggest one.
Breaking this problem down into logical steps (assuming we've already solved the problem of getting the input into our program):
- split the input on double line breaks to get each collection
iterate through the collections
- split them on line breaks to get each value
iterate through the values
- parse them as numbers
- add them together
- return the sum
- find the maximum value among the totals for each collection
Part Two sees us taking the same logic, but sorting the collection and adding the top 3 totals together.
What's great about a simple challenge like this one for learning a new language is that it lets you play around with concepts that you are likely already familiar with and just sort of familiarize yourself with the syntax and how you execute programs in this new language.
Things may not have been perfect, but this felt like a very successful start with the language.
Rust is harder than I thought
I guess I do need to read about this whole borrowed vs. owned thing
Woah, this IDE integration with the
I'm so glad I fell in love with TypeScript before I tried to pick up Rust
Why can't I join these numbers into a string? I guess I'll just dump the whole array.
I need to read the instructions more carefully.
Here are the actual search strings I used, taken directly from my browser's history.
rust read file input
rust concatenate strings with variables
rust analyzer failed to discover workspace
rust import from another file
rust string vs &string
rust match bindings cannot shadow statics
rust default value for argument
rust split string
rust sum of array
rust number types
rust apply function to each item in vector
rust sort array
cannot return value referencing temporary value
rust sort descending
You can pretty much reconstruct how I arrived at my solution for Day One by following that trail of search strings.
I still had lots of things to figure out, like:
- How to implement verbose debugging messages without needing to manually flip a Boolean in the code
- How to pass named parameters to the executable
- How to actually test the output of the executable against the example solution when providing the example input
Day Two has us tackling a Rock, Paper, Scissors game with some complex scoring rules.
This is when I learned about the second thing I love about Rust: Features.
There are certainly ways to achieve this in every language, but this might be the simplest and easiest to implement that I've encountered.
Once again, we're given an example input:
And told that each line represents a round of Rock, Paper, Scissors and that we need to model the outcomes and provide the relevant total score based on the game's outcome and our chosen weapon.
Breaking this problem down into logical steps:
iterate through each line
- get the other player's choice (first column)
- get our choice (second column) and its associated point value
- execute the game's rules to decide who won
- get the value of the round for us (based on win, loss, or draw)
- add the value of the outcome and the value of our choice
- return that sum
- add up all the individual round sums to get our final score
Day 2's puzzle is the first to introduce the Advent of Code trope of Part Two changing how you interpret the input, and asks us to figure out what choice we should make in each round to achieve a desired outcome and then provide the final total score based on those choices.
It requires us to change things a little bit, but most of the logic is still there.
This one was also great to start groking the ins and outs of Rust because it takes a game we're likely already familiar with, adjusts it minimally, and makes us think about how to model those rules programmatically.
Rock, paper, scissors is actually a little harder than I thought; the point system here makes it a little more complicated, too
I know there has to be a better way than all these ifs, but I guess I'll learn that later
I still have no idea when I'm supposed to use
&and when I'm not; I'm really glad this IDE integration exists
Here are the actual search strings I used, taken directly from my browser's history.
rust println! in debug only
rust how to get feature
rust trim string
rust get value from enum
rust import macro from another file
rust match multiple statements
rust remove empty elements from vector
- Okay, it's time to actually dig into this borrowed vs owned thing
- I need to take some time to learn about the differences with
- Am I overusing
.iter()? Do I need them as often as I have been using them? When should I chain methods and when should I not?
Day Three has us comparing parts of a string and looking for duplicates.
This time I decided to try to make myself to explore the available functions in my IDE's Intellisense suggestions before searching for a solution to something. I think it worked out okay, and as a result you'll see that there are a lot less search strings focused on a specific task - and a lot more focused on a specifc error message I received while working in this way.
We're supposed to split these strings in half and find the character that appears in both halves, then convert that character to a numerical value and add them all together.
This one sort of reinforces what we've already started learning and gives us a chance to explore those concepts a little further.
So far, using Advent of Code to learn a new programming language seems like a good idea, but I worry when the difficulty really increases it will result in a wall of frustration.
Okay, we're doing another reduce/sum of an Array; hopefully I get the hang of the language before we get into more complex structures
That Rust Demystified video sort of actually helped me understand the
&and I kind of get some of the lifecycle stuff now, too - I definitely need to watch it a couple more times over the rest of the month to come back and pick up more
I cannot figure out why it seems so hard to map through stuff in Rust; I must be doing something really wrong or doing things in a way that isn't idiomatic
You may notice that many of the search strings are mostly error messages that are getting a little more complex - not sure what the takeaway from that is just yet.
a value of type `&str` cannot be built from an iterator over elements of type `&str`
consider introducing a named lifetime parameter: `<'a>`, `'a `, `'a `, `'a `, `'a `
this function's return type contains a borrowed value, but the signature does not say which one of `pockets`'s 3 lifetimes it is borrowed from
a value of type `Vec<&str>` cannot be built from an iterator over elements of type `Vec<&str>`
the trait `FromIterator<T>` is implemented for `Vec<T>`
rust vector remove duplicates
rust usize to i32
rust expected slice `[&str]` found tuple `(_, _, _)`
refutable pattern in function argument
cannot return value referencing local variable
- Figure out if Vectors are even the right approach for this kind of thing; I think I'm doing it wrong
- Revisit the ownership thing again - it still hasn't sunk in yet
- Look into lifetimes more, I sort of understand why I needed to add a named lifetime (like
<'a>) but it's still kind of unclear, too
Day Four has us converting some strings into number ranges and seeing if they overlap completely.
I started off the day by trying to clean up my very naive approach to the two parts of a puzzle per day pattern and think about a more Object-oriented approach to setting up each day's solution, but ended up deciding I didn't quite grok enough of the language's idioms yet and decided to keep pushing forward with the current status quo for now.
I also wanted to switch from the
cargo run --features test approach that I took and use the built-in
#[cfg(test)] functionality so that I can just run
cargo test, but will tackle that another day.
Once again we're doing a lot of splitting of strings here. We could probably do something with a Regular Expression if we wanted to, but I'm not ready to dig into if/how RegEx is handled in Rust just yet, so we'll probably naively:
- Split on
- Split on
- Split on
- Create a range for each number sequence
- Check both ranges to see if either fully contains the other
The second part introduces another common Advent of Code puzzle trope: Switching between a complete and partial match. So we just need to flip our
.all() to a
.any() and we're good to go.
The first part of this one gave me a chance to try to apply what I have learned so far and the second part gave me an opportunity to refactor and make my code more reusable, but I was lazy and just duplicated it instead of trying to follow the Don't Repeat Yourself (DRY) principle and clean things up.
I was able to do this all without too much trouble, but the code is definitely not what I would like it to be.
I actually felt like I kind of knew what I was doing this time. That might be some progress.
// still haven't mentally accepted this one, I guess
rust default function argument value
rust check if two vectors contain same elements
rust create array from range
- Should really be using
cargo testinstead of the
cargo run --features testapproach
- Would love to use more of the language's features (
- Need to learn more about handling
Option<T>types and best practices for when and how to
Well, that escalated quickly. Day Five introduces another Advent of Code trope: data visualization.
I started off thinking about making one of those fancy data viz solutions you see on the subreddit, but decided that I should probably just catch up to the current day first instead.
[Z] [M] [P]
1 2 3
move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2
This one is a bit more challenging because we have to parse the input in multiple different ways. We need to understand the total number of stacks we are dealing with and which item is in which stack while also parsing the instructions for moving items from one stack to another.
Breaking this down we're going to want to:
- Split on
"\n\n"to separate the stack visualization from the instructions
- Pop the stack numbers from the stack visualization so we know how many stacks we need to keep track of
- Parse the crates in each stack
- Parse each instruction and manipulate our stacks
Note: One thing I've learned over the years with Advent of Code is that you can simplify and reduce your chance of bugs by just inserting an empty item as the first element in your Arrays when you are dealing with things indexed at
1 like these stacks of crates. Rather than having to constantly convert indices, you can just go with what's written and then filter out the empty item at the end if necessary.
And part two has us pulling multiple containers off the stack instead of doing them one by one, which is similar to the Advent of Code trope from Day Four, except this one has to do with moving from processing a stack as individual items to processing it as groups of items. Luckily most of the logic is still the same.
The most challenging part of this one was actually parsing the initial stack state correctly. It also introduced another Advent of Code trope: the reduced example case doesn't have edge cases that your real input might have (like multiple empty spots in a row so it isn't quite as easy to make sure you put the right crate on top of the right stack).
This one ramped up the complexity a good bit; there usually seems to be a bit of a hockey stick in terms of the difficulty scaling - and that's often reflected in the decreasing number of stars awarded.
Let myself get too behind by overthinking and messing this one up at first
// I ended up not using this one
rust update output in console
// stuff I actually used
rust regular expressions
rust for in get index
rust iterate n times
rust remove first item from array
rust can't compare `&str` with `str`
rust borrowed value does not live long enough
rust compar char to str
- Should really switch to using
cargo testinstead of the
cargo run --features testapproach I ended up with
- Get better at making reusable functions for this stuff (I've been avoiding it due to some lifetime and ownership issues) so that these kind of day two slight tweaks can just be a new function or parameter to an existing function instead of repeating so much code
Five days down; twenty to go.
I feel like I've already learned a lot and am starting to feel more comfortable and less frustrated with the language, but I know there's a long way to go before I'll be writing idiomatic Rust, much less efficient Rust.
I've always tried to follow the "Make it work. Make it Right. Make it Fast." philosophy, and feel like I'm definitely still in the
Make it work. phase and probably won't make it to the
Make it fast. phase during this exercise, but that's okay.