2023-12-29: Learning Rust through Advent of Code


I completed day one of the 2023 advent of code in rust to try learning the language.

The main reason I’m interested in rust is because I’m happy being a python developer, and see a trend of rust being used to create durable “cores” for python projects as seen in in pydantic 2.0 and ruff. A synthesis of the two language seems like the best of two worlds since python truly is a joy to write. Plus I’d like to explore embedded programming in rust.

Completing the challenge took me longer than I would have liked, even with ChatGPT help. I started by generating a list of digits and replacing the values, but this missed strings like "threeight" -> "38" where eight would be matched to 8 when finding the last digit. So I started coding in anger and made a really dumb solution where I hardcoded all the number matches and reversed the string. It worked and that’s all I really wanted.

From here on I’d like to see how I can reduce the memory footprint and runtime as a practice in benchmarking software. How would things be improved by using &str instead of String? Does using u8 vs u32 where appropriate actually do anything?

Enjoy the solution below.

use crate::adventofcode::utils::get_aoc_input; 
use std::error::Error;

fn walk_and_find_digit(input: String, backward: bool) -> u32 {
    let mut current_str: String = input;
    if backward {
        current_str = current_str.chars().rev().collect();
    }

    while !current_str.is_empty() {
        for &(word, digit) in NUMBER_WORDS.iter() {
            let match_word: String;
            if backward {
                match_word = word.chars().rev().collect();
            } else {
                match_word = word.to_string()
            }

            if current_str.starts_with(&match_word) {
                return digit;
            }
        }
        current_str = (&current_str[1..]).to_string()
    }
    0
}

static NUMBER_WORDS: &[(&str, u32)] = &[
    ("1", 1),
    ("2", 2),
    ("3", 3),
    ("4", 4),
    ("5", 5),
    ("6", 6),
    ("7", 7),
    ("8", 8),
    ("9", 9),
    ("one", 1),
    ("two", 2),
    ("three", 3),
    ("four", 4),
    ("five", 5),
    ("six", 6),
    ("seven", 7),
    ("eight", 8),
    ("nine", 9),
];

/// Given a string, convert any english words in the strings into numbers.
/// Ie, "twone" -> "2ne"
///
pub fn p2() -> Result<u32, Box<dyn Error>> {
    let input_lines = get_aoc_input(2023, 1)?;

    let mut total_sum: u32 = 0;

    for line in input_lines {
        let first_digit = walk_and_find_digit(line.clone(), false);
        let last_digit = walk_and_find_digit(line.clone(), true);
        let computed_number = first_digit * 10 + last_digit;
        println!(
            "{}: {} {} -> {}",
            line, first_digit, last_digit, computed_number
        );
        total_sum += computed_number;
    }

    println!("P2 Total sum of calibration values: {}", total_sum);
    if total_sum != 55130 {
        panic!("P2 FAILED")
    }
    Ok(total_sum)
}

todo: add more css padding on the code block above 👆

See also