I will be attempting Advent of Code 2022 with Rust, a language I have never learned before.

Overview

Advent of Code 2022

This year, I will be attempting Advent of Code with Rust, a language I have never learned before.

I will also be taking some notes while I learn the language!

Day 1

Projects

Create new Rust project by cargo new <project-name>. This will create directory project-name, which is the project root directory. Within is a file ./Cargo.toml. Here is where you put the dependencies. There is also a ./src directory, where source code goes in. There exists a main.rs already.

To build the project, navigate to the project root and run cargo run.

Rust code

Import items using use ..., such as use std::fs and use std::env.

We can read from file using fs::read_to_string(file_path) -> std::io::Result<String> (docs), which in turns also contains the potential error in case it fails (i.e. it doesn't throw RuntimeError). We can do something like this to handle errors:

let contents = fs::read_to_string(file_path).expect("err: err_msg");

For string processing, refer to std::string::String docs. In particular, s.split(...) returns an iterator which we can directly iterate over. Also, we can convert string into integers by s.parse::<i64>() (handling error with .expect) or other types instead of i64.

Rust also supports a match syntax for handling error. For example,

let n = "10";
match n.parse::<i64>() {
    Ok(n) => println!("{n} is a number!"),
    Err(e) => println!("{e} is NOT a number!"),
}

Of course, Rust supports dynamic size data structures such as Vec. It is defined in std::vec, but a set of things are auto imported in every program, as defined in std::prelude. The full list can be found here. One important method of Vec is .sort_unstable_by, which takes a mutable comparator that returns an Ordering (FnMut(&T, &T) -> Ordering) as argument.

Day 2

In today's code, I also used the match syntax from before, this time to unpack / map values. For example, if we know the variable vec: Vec<usize> is of length 2, then we can unpack it into two usize variables a and b by writing

let (a, b) = match vec[..] { // matches tuple (a, b)
    [a, b] => (a, b),        // if vec is of pattern [a, b], then returns (a, b)
    _ => unreachable!(),     // unreachable
};

Also, the syntax of declaring a function is fn <func-name>(<arg-name>: <type-name>, ...) [-> return-type]. For example,

fn enhance(mut pixels: Vec<Vec<usize>>, r: usize, c: usize) -> Vec<Vec<usize>> {
    // ...
}

Other than that, the syntax required for today's problem is not too advanced.

Day 3

In today's code, I had to implement finding common elements between two strings. At first, I tried something like

fn intersect(s1: str, s2: str) -> impl Iterator<Item = char> {
    s1.chars().filter(|c| s2.contains(&c.to_string()))
}

However, I run into lifetime issues. I have not had time to fully understand what lifetime is yet, but essentially currently, s1.chars is tied to s1. Since .filter returns an iterator that points into s1.chars(), this causes an issue as the compiler does not know when the memory location at &s1 will be invalidated. Therefore, we have to specify the lifetime of the objects, which tells the compiler how long the objects should live for i.e. how long they will stay valid. There are two basic syntaxes for lifetime: '_ and 'a, where the former is essentially an anonymous / generic form of the latter. Therefore, we can fix the code above by writing

fn intersect<'a>(s1: &'a str, s2: &'a str) -> impl Iterator<Item = char> + 'a {
    s1.chars().filter(|c| s2.contains(&c.to_string()))
}

Also, note that this problem goes away by converting the iterator into a collectable via .collect(), since it essentially clones each object, so the lifetime of the objects in the collectable is tied to the collectable instead.

By the way, the intersect function above is not used in the final solution, as part 2 requires intersecting 3 strings, so I figured out that it is probably easier to write a function with signature fn intersect(v1: Vec<char>, s2: Vec<char>) -> Vec<char>.

Day 4

Today's problem is straightforward, but I learned quite a lot of features from Rust. Firstly, I created a struct Interval to wrap the endpoints of an interval in a nice data structure. In Rust, it is not possible to define object constructors, as everything should be "explicit". Therefore, I also created a make_interval function for that.

Next, I had to check whether an interval covers another intervals entirely, which involves swapping the intervals when the left endpoints are not sorted. This functionality is implemented in std::mem::swap. However, due to the "assign once only" property of Rust variables, I have to pass in mutable pointers into the functions instead. The code looks like this in the end:

fn cover_entire(x: &mut Interval, y: &mut Interval) -> bool {
    // [1, 4] -> [1, 2], [2, 3] but not [2, 5]
    if x.l > y.l {
        mem::swap(x, y);
    }
    (x.l == y.l) || (x.r >= y.r)
}

// ...

cnt += cover_entire(&mut interval1, &mut interval2) as usize;

Also since std::mem is part of the standard library, I did not have to modify the Cargo.toml file.

Day 5

In today's code, I tried writing it in a way to minimize code repetition. This is because the only part that changes between the two parts is the order of which elements are placed onto the destination stack. Therefore, I created an enum OperationOrder and a function fn solve(option: OperationOrder) that computes the answer based on the given order. In particular, the only code that depends on the operation order is:

enum OperationOrder {
    FIFO,
    FILO,
}

// ...

// tmp: VecDeque<char>
let element = match option {
    OperationOrder::FIFO => tmp.pop_front(),
    OperationOrder::FILO => tmp.pop_back(),
}

I also printed the "Part X" prompt directly, as it is implied by the operation order. To do so, I followed this and implemented the std::fmt::Display trait for the enum as follows:

impl fmt::Display for OperationOrder {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            OperationOrder::FIFO => write!(f, "Part 1 (FIFO): "),
            OperationOrder::FILO => write!(f, "Part 2 (FILO): "),
        }
    }
}

Also, I believe some parts of the code could be made more efficient. For example, in this part of the code,

// moves: &str
for line in moves.split("\n").collect::<Vec<_>>() {
    let iter = line.split(" ").collect::<Vec<&str>>();
    let args: Vec<usize> = match iter[..] {
        [_, x, _, y, _, z] => [x, y, z]
            .iter()
            .map(|&c| c.parse().expect(INT_ERR))
            .collect(),
        _ => unreachable!(),
    };

    // ...
}

There are a lot of conversion between iterators (result of .split()) and collectables (Vec). Also, it is probably inefficient to write [x, y, z].iter().map(func).collect(), as it is equivalent to [func(x), func(y), func(z)]. However, the definition of func here is quite long, and I am not sure whether "inlining" the definition into the code is a good idea.


Day 5.5

I have modified my solution for day 5 quite a bit. Firstly, I used itertools::Itertools, which includes an implementation of the trait .next_tuple() for Iterators. This allows me to extract tuples directly after splitting the string, instead of matching [_, x, _, y, _, z] => [x, y, z] then matching it into tuple. In order to keep the "method apply chain" going (l.split_whitespace().next_tuple().expect(...)), I defined an Applicable trait for every type by impl<T> Applicable for T. This gives access to the method .apply(f), where f: Fn(Self) -> T is a closure. This allows me to apply a closure in a sequential order, rather than wrapping everything in f(...).

Another change I have is replacing the push_back and tmp: VecDeque<char> with direct method calls to the vectors via .truncate and .extend.


Day 6

Today's problem is a simple one - find the first index where s[i:i + k] contains distinct characters. I implemented an optimization where instead of moving the sliding window one index at a time, we shift it to after the first match. For example,

[d  a  b] b  c  a  -> no match

 d [a  b  b] c  a  -> match! (relative) idx = 1
       ^
 d  a  b [b  c  a] -> distinct

We can skip to after idx, indicated by ^, because any window containing ^ must contain the character that matches with it, meaning the sliding window will contain duplicate characters.


Day 7

LOL THIS TOOK ME 5 HOURS

Short notes for now: I changed the structure of the code from two separate solve_part_X() functions into a single Solver::solve() function. Implemented a Tree structure that took surprisingly long, around 3 hours spent on both .dfs and getting TreeNode to include a parent field. Googled and realized it is not possible due to lifetime issues... Fixed .dfs(&closure) infinite recursion in compilation phase error by passing in the closure as a &dyn.

Day 8 -

I feel like I am familiar enough with Rust now that taking notes is not helpful anymore for me, at least not in the way I have been taking notes above. Just some updates, I started a few Rust projects, including:

  • rust-dfa-nfa-regex, converting between DFA, NFA and Regex, along with some graphics for the graphs

  • rust-graphics, messing around with Rust, wasm and graphics in general. Planning to make some visualization of something.

  • @rust-lang/rust! I am looking into the compiler and see if I can contribute anywhere. Currently working on the diagnostics part of the compiler, hoping to generate better warnings and errors.

You might also like...
πŸŽ„ My solutions to Advent of Code 2022.

πŸŽ„ advent-of-code-2022 Overview This repository contains my solutions to Advent of Code 2022. I decided to take this year as an opportunity to learn R

Solutions to Advent of Code 2022 puzzles

Solutions to Advent of Code 2022 puzzles

πŸŽ„ Advent of Code written in various languages. (2015-2022)

πŸŽ„ Advent of Code This repository contains my solutions to the Advent of Code puzzles. Every year, I will try to solve the puzzles in a different lang

Here are my Advent of Code solutions for 2022.

advent-of-code-2022 This repository contains my Advent of Code solutions for 2022. I am doing them in Ruby, Crystal, and Rust, and as I release each d

Advent of Code 2022 - my answers

Advent of Code 2022 my answers Development occurs in language-specific directories: Haskell Kotlin Python Rust Day1.hs Day1.kt day1.py day1.rs Day2.hs

Rust-advent - Learning Rust by solving advent of code challenges (Streaming live on Twitch every Monday)
Rust-advent - Learning Rust by solving advent of code challenges (Streaming live on Twitch every Monday)

Rust advent πŸ¦€ 🐚 Learning Rust by implementing solutions for Advent of Code problems. πŸŽ₯ HEY, we are live-streaming our attempts to solve the exercis

Catch Tailwindcss Errors  at Compile-Time Before They Catch You, without making any change to your code!  Supports overriding, extending, custom classes, custom modifiers, Plugins and many more πŸš€πŸ”₯πŸ¦€
Catch Tailwindcss Errors at Compile-Time Before They Catch You, without making any change to your code! Supports overriding, extending, custom classes, custom modifiers, Plugins and many more πŸš€πŸ”₯πŸ¦€

twust Twust is a powerful static checker in rust for TailwindCSS class names at compile-time. Table of Contents Overview Installation Usage Statement

You'd never guess what this did.

FlashbangVST A plugin I made to test out making VSTs in Rust. This is possibly one of the best things I have ever made. If you make something using th

Get your loadshedding schedule in your calendar and never be left in the dark! Open-source, up-to-date, and developer friendly.
Get your loadshedding schedule in your calendar and never be left in the dark! Open-source, up-to-date, and developer friendly.

Loadshedding schedules in your digital calendar. No apps, no ads, up-to-date, and developer friendly. Get it β€’ Key Features β€’ Using the data β€’ Project

Owner
null
Advent of Code 2022 (Rust)

Advent of Code 2022 ?? | -+- A /=\ /\ /\ ___ _ __ _ __ __ __ i/ O \i / \/

Dominik Harmim 2 Dec 15, 2022
Advent of Code 2022 Solutions (in Rust)

Advent of Code 2022 My solutions for Advent of Code 2022, written in Rust. This repository provides a good template for anyone interested in writing t

Sam Mohr 3 Dec 2, 2022
My solutions for Advent of Code 2022, written in Rust

Template largely copied from RikvanToor/aoc-rust-template, upgraded for clap v4. This template can be forked/cloned from beeb/aoc-rust. Usage First, f

Valentin Bersier 2 Dec 15, 2022
Advent of Code 2022 in Rust πŸ¦€

Advent of Code 2022 in Rust ??

Philippe Bouamriou 3 Dec 2, 2022
πŸ¦€ Rust solutions for Advent of Code 2022

?? Advent of Code 2022 Solutions for Advent of Code in Rust. 2022 Results Day Part 1 Part 2 Day 1 ⭐ ⭐ Day 2 ⭐ ⭐ Day 3 ⭐ ⭐ Day 4 ⭐ ⭐ Day 5 ⭐ ⭐ Day 6 ⭐

Felix SpΓΆttel 10 Dec 28, 2022
My try at Advent of Code 2022 in Rust.

Advent of Code 2022 My solutions to Advent of Code 2022, written in Rust. I love seeing the variety of solution other people come up with, so I decide

Luke Taylor 6 Dec 15, 2022
Solutions to Advent of Code 2022 in Rust Β πŸ¦€

AdventOfCode2022 ?? Solutions to AoC 2022 in Rust ?? Usage cargo run --release --bin DAY # run a specific day cargo run --release # run all

Axel Lindeberg 32 Jan 5, 2023
πŸŽ„ My Advent of Code solutions in Rust. http://adventofcode.com/2022

Advent of Code 2022 in Rust My Advent of Code 2022 solutions in the Rust programming language. This repository holds a separate Rust project for each

Tim VisΓ©e 98 Jan 4, 2023
Advent of Code 2022 - in Rust!

Advent of Code 2022 My solutions to problems in 2022 edition of Advent of Code, written in Rust. How to use this repo: The best way to see how solutio

Marcin Grzywaczewski 4 Dec 22, 2022
My Advent of Code 2022 solutions, in Rust.

AoC 2022 My solutions for the 2022 edition of Advent of Code in Rust. Simply run cargo run --release --bin dayXX to solve. Some days require z3, insta

Orson Peters 6 Jan 26, 2023