My Advent of Code 2020 solutions, implemented in Rust and built using VS Code.
A list of the puzzles, and what new language/tool features I learned each day:
- Basic Rust + VSCode integration
- Basic Rust Git configuration (
.gitignore
,.gitattributes
) - Reading text files line-by-line with
BufReader
- Parsing strings as integers with
my_str.parse()
Vec
for growable lists
- All-at-once text file parsing using
fs::read_to_string()
. Nothing this month should be large enough to warrantBufReader
. String::lines()
to iterate over lines of text data.- Unit tests with
#[test]
- Raw strings with
r"blah"
- Indexing into a string's
char
s is verbose. For AoC purposes, we'll assume ASCII and 1u8
= 1 character. - Basic use of the
regex
crate -- checking for matches, named capture groups, etc. - How to set up Cargo for multi-target packages. Less duplication of target metadata this way.
- Q: Why is VS Code inserting suggestions after
.
or:
in comments? - Q: How to generalize this code into a template more easily applicable to future days' problems?
- Basic 2D grid struct with new/get/set methods
vec!
macro for array-likeVec
declarations${fileBasenameNoExtension}
substitution works inlaunch.json
(no more day-specific debug configurations!)
- Debugging Rust unit tests in VS Code (use the target/debug/deps/project-hash.exe executable, run with
--test-threads=1
to keep the callstack from jumping around) - Basic HashMap usage
- Custom message for assert failures
- match statement, with non-fatal
None
handling forOption<T>
- radix conversion with
u32::from_str_radix()
- in-place array sorting:
a.sort()
std::cmp::max(a,b)
andstd::cmp::min(a,b)
- nothing new, really
HashSet
for value-less hash mapsString.split()
returns an iterator, not a collection- Started to run into lifetime issues. I can solve them by just making things
String
until they go away, but that's not sustainable.
- I made an
enum
. Though in this case it could've been a struct in the end, since NOPs have args as well. - Q: how to safely add a signed integer to a
usize
? (typecasting around feels like it misses the point) - I feel like
HashMap.entry()
could save me some redundant lookups, but performance hasn't been an issue yet.
- Some basic functional programming with
.map()
and lambdas - Q: How best to pass side-channel data into the "solve" functions, such as the history size for part 1, or the target sum from part 2? I guess an
enum
of some sort?
- Hash map keys must be borrowed. Even if they're literals. (Q: why?)
HashMap.Get(&k).unwrap_or(default)
for potentially missing values
std::mem::swap()
to exchange two mutable locations without deinitializing- No ternary operator;
if-else
expressions have a value instead: `x = if (c) { a } else { b }; - integer casts:
usize::try_from(n).unwrap()
#[rustfmt::skip]
on a function/etc. to opt out ofrustfmt
- absolute value is a method on integer types, e.g.
-3.abs()
- No Python-esque tuple assignments; if
wx
andwy
exist, you can't swap withwx,wy = wy,wx;
- Probably rediscovered some modular arithmetic theorem? (Sure enough!)
- Rust calls reduce()
fold()
. - Successful use of
HashMap::entry()
:*memory.entry(addr).or_default() = val;
- HashMaps are slow in debug builds.
- Need to come back and reoptimize/clean this up so I can re-enable the unit tests for part 2, I'm doing a gajillion unnecessary lookups.
cargo clippy
! Went back and fixed all the clippy lints in previous days' solutions.- Use 64-bit integers everywhere!
- HashSet
- static constant arrays
- Tuple structs
- Initializing collections with
.with_capacity()
instead ofnew()
if an upper-bound (or exact) size is known at creation time.
- Some new string iteration functions:
.matches()
,.match_indices()
,.position()
, etc. But, definitely not clear which are defined on which string types. - I am not good at
String
/str
/&str
/[u8]
manipulation in Rust :(. In part 2 especially, I'm creating newString
s all over the place just to keep the borrow checker happy.
- Iterator::any() and all()
- Escape curly braces in format strings with
{{
and}}
- Implementing
Display
trait for custom struct - Manually advancing an iterator mid-loop, using
next()
andwhile let Some(_) = iter.next()
collect()
doesn't seem to work on iterators over&str
, which is unfortunate.sort_by()
on collections, for custom sort functions- I tried to go more functional this time where possible.
DefaultHasher
for a 64-bit hash of arbitrary bytes (even provided incrementally). Handy!
- Man I wish indexing arrays by non-usize wasn't a constant pain
- This is getting more natural!
- Anticlimatic