Advent of Code #2: Borrows, unwraps and lots of compiler errors
First full week of learning Rust with Advent of Code is in the bag and I'm so happy I've gotten this far. Due to the pandemic, there's more time to get these done every day and that's really helping me finish. Here are my thoughts from days 2 to 8.
Feeling Rust-y?
I didn't remember learning was this tough. I did talk about it bit in the first post of the series but this week definitely showed its true colors to me. Have you played those video games where in the intro, you get to play with all the gadgets and abilities and then they are taken away? I feel like that right now.
I see a task (like reading the input data into a proper data structure) and I feel confident because with languages like Python or Javascript it would take me a few seconds and no retries to get it done. With Rust, it's a different story. Since I'm not yet familiar with the standard library and what each type has in its toolbox, I struggle a lot with stuff that I normally don't even break a sweat. Splitting a string and mapping over that data? Yeah, I'll either run into a hundred compiler errors or google furiously to figure it out. And it's so frustrating.
My inner compass on what types different operations return and especially when to reference with &
, when to use variable as-is and when to dereference with *
is basically mostly still based on me guessing and/or following compiler errors.
I've started to call it Compiler Error Driven Development. It leads to functioning software, which I'm really happy about, given how much I give talks and workshops on debugging but it doesn't always feel great and it definitely doesn't create idiomatic Rust code.
So much unwrapping and it's not even Christmas Eve yet
One of the new things for me is the use of Option
type. Instead of returning a number, I'd return a Some(number)
and then do a lot of unwrap
ping on the other side. On a theory level, I do kinda understand it but it's difficult to wrap my head around because it still feels like a lot of boilerplate code with all of the operations I run.
On Day 2, I laughed at the amount of unwrap
s in my code (it was the first time I really used them a lot) and it was such a good Christmas pun that it made me feel Christmas-y. I replaced some of them with ?
s but I still don't have a solid understanding of which one to use and when and what are the implications with the rest of the code with Option
s and Result
s.
Different approaches to learning
I've been mostly on the other side of the table over the past years. I've been teaching Python and Javascript and helping junior developers get started with software development. Within the world of Python and Javascript, my learning has mostly been about reading the docs and figuring out how a library works.
With Rust, I'm in a whole new situation. And since I have a strong background with a few (very different) languages, I've noticed that I'm much more looking for best practices than how to run the code type of advice. I can make the code run and return a result (so far, the right result) but I feel utterly lost not knowing if my code makes any sense in terms of writing Rust code.
Luckily, Rust seems to be a choice of language for many people this year with Advent of Code so reading through the solutions of multiple different people as well as getting advice and code reviews from fellow developers each day has been very helpful. And for once, I've been merciful to myself as I'm so early in the learning process.
Borrow this, borrow that
Last week I mentioned how my experience with languages like C, C++ and Rust is basically zero. I remember my struggle with references and pointers and all that from that only time I touched C++ in university. And it's all coming back to me with Rust.
Week and a half into my Rust journey and I still most of the time just guess what kind of prefix my variables need and when. The Rust compiler is very kind and I tweeted last week about how the language matters. When I'm stressed about all the red in my terminal, a kind tone of voice the compiler has definitely makes it easier to deal with.
On Day 3, I especially got so many "You're borrowing here" and I just couldn't understand what the concept meant. I was directed to do some more reading and finally I understood what it means – even though it doesn't mean I still know how to do it right the first time. But on some level, I do now know what borrowing, moving and ownership mean in terms of Rust and variables.
As the week progressed though, I have to say that the very technical parts of Rust compiler errors started to get bit challenging to follow and increased the stress factor.
Day 4 threw wrenches into many people's work
I struggled so much with Day 4. I got the first part done quite nicely but then the second one stumped me totally. And talking with others, I wasn't alone. Interestingly enough, for most of the people I talked with, the exact issues were unique to each one of us but all very related.
The solution for all of us was the same though: reading through the specification more carefully. I had a dozen nicely working tests and manual inspection of the debug output seemed to be right as well but still Advent of Code kept telling me I was wrong. I had missed a part of the specification (or more accurately, I didn't realize the input for height could be missing the cm
/in
part).
One thing I realized when struggling with it is how nice it's to do these Advent of Code puzzles that have very well defined inputs and outputs and rules. In real life, the situation is very different and too often we end up developing something with very unclear requirements. So AoC is great change of scenery for that.
A few nice days before the Big Challenge
Then days 5 and 6 were really nice for me. Maybe I had gotten a slight bit better with Rust or the puzzles ended up being in my wheelhouse but I basically cruised through them in a short time with almost no issues at all.
That was all nice and good but then came Day 7. I read through the puzzle on Monday but decided to do other things as I wasn't feeling super puzzle-y that day. When I returned to it on Tuesday, I just ended up hitting brick wall after brick wall.
The combination of me struggling with Rust (especially with manipulating collections like vectors and hashmaps inside loops), the fact that I've never really done anything with graph or tree structures and a challenging puzzle and I spent hour after hour after hour trying to make something happen but failing over and over again. I can't remember when was the previous time I've felt that frustrated.
I got a lot of help from people at Futurice and Koodiklinikka (especially thanks to Joonas K. for patiently walking me through stuff) but I had to admit my limitations this time. Day 7 defeated me triumphantly.
Bounce back with 8
On the other hand, solving day 8 was once again fun and somewhat straight forward. I got my first solution for part 2 by cheating a bit (I ran the code once for one option and then manually changing the code and running again) since I wasn't quite sure my base solution was gonna be right. Once I learned it indeed was right, I was able to refactor the code to do that automatically without me having to change things in the code.
It was also probably the most interesting to me in terms of what was being built and I hope we get to do something similar in the future as well.
Every now and then during the week I felt like I was learning Rust and then every now and then I got cold water to my face and realized I still don't understand how it works or how to code with it.
As the month goes along and I hopefully learn a bit more, I'll definitely revisit my thoughts on Rust each Wednesday on these blog posts. And since these weekly posts are starting to end up being very flow of thought journaling more than well edited blog posts, I'll definitely write a more comprehensive wrap up post at the end of all this to collect my thoughts.