DEV Community

Cover image for Advent of Code 2019 - Day 1 (an aside, in Rust!)
bretthancox
bretthancox

Posted on • Edited on

3 2

Advent of Code 2019 - Day 1 (an aside, in Rust!)

Introduction

Since I'm dabbling with Rust, I wanted to see if I could do some of the Advent of Code using it. I'm nowhere near as proficient in Rust as I am in Clojure, so I don't expect to do some of the later challenges, but Day 1 is relatively friendly.

Day 1.1

I did one very simple approach and one that used functions implemented on a custom type. The latter is overkill for this, but it scales well when inputs are used multiple times. Implementing functions on the type reduces boilerplate later.

Anyway, here's the simple:

fn main() {
    let day1: Vec<u32> = vec![88093, 102524, ***SHORTEN*** 54128, 111698, 84299];
    let total_fuel_reqs: u32 = day1.into_iter()
                                   .map(|x| ((x / 3) as u32) - 2)
                                   .sum();
    println!("{:?}", total_fuel_reqs);
}
Enter fullscreen mode Exit fullscreen mode

And here's the over-complicated version:

pub struct Day1Inputs{
    day1_input: Vec<u32>
}

impl Day1Inputs {
    fn day1_1(self) -> u32 {
        let total_fuel_reqs: u32 = self.day1_input
                                       .into_iter()
                                       .map(|x| ((x / 3) as u32) - 2)
                                       .sum();
        total_fuel_reqs
        }
    }

fn main() {
    let day1 = Day1Inputs {day1_input: vec![88093, 102524, ***SHORTEN*** 54128, 111698, 84299]};
    let fuel_req = day1.day1_1();
    (println!("Day 1: {:?}", fuel_req));
}
Enter fullscreen mode Exit fullscreen mode

Day 1.2

I stuck with the implementation approach for this part. Recursion is a little less easy in Rust than Clojure (mainly because Clojure lets you stick a recur at the end of your loop and does everything else for you). As such, I built a recursion into day1_2.
One copy event to create a local version of the object mass that can be reduced with each loop as the new fuel mass is returned: fuel_req
One variable to catch the aggregating mass for the individual object and its fuel: total_fuel_mass_for_object
One variable to catch the aggregating mass for all objects: total_fuel_mass

An extra function was needed to hold the fuel calculation this time: day1_2_fuel_calc.
You'll notice that this function can't be called with self as it doesn't use self as an argument. This means it must be accessed via the double-colon approach: Day1Inputs::day1_2_fuel_calc

All previous u32 values were switched to i32 as the recursive fuel calculation can produce negative integers causing the code to panic if it is dealing with u32.

Then I create a new instance of the type since Vec can't implement Copy, preventing me from passing it to the day1_2 code after using it in day1_1.

pub struct Day1Inputs{
    day1_input: Vec<i32>
}

impl Day1Inputs {
   fn day1_1(self) -> i32 {
        let total_fuel_reqs: i32 = self.day1_input.iter().map(|x| ((x / 3) as i32) - 2).sum();
        total_fuel_reqs
        }

    fn day1_2_fuel_calc(mass: i32) -> i32 {
        let new_mass = ((mass / 3) as i32) -2;
        new_mass
    }

    fn day1_2(self) -> i32 {
        let mut total_fuel_mass: i32 = 0;
        for mass in self.day1_input {
            let mut total_fuel_mass_for_object: i32 = 0;
            let mut fuel_req = mass;
            loop {
                fuel_req = Day1Inputs::day1_2_fuel_calc(fuel_req);
                if fuel_req <= 0 {
                    break;
                }
                total_fuel_mass_for_object = total_fuel_mass_for_object + fuel_req;
            }
            total_fuel_mass = total_fuel_mass + total_fuel_mass_for_object;
        }
        total_fuel_mass
    }
}


fn main() {
    let day1_1 = Day1Inputs {day1_input: vec![88093, 102524, ***SHORTEN*** 54128, 111698, 84299]};
    let fuel_req = day1_1.day1_1();
    (println!("Day 1.1: {:?}", fuel_req));

    let day1_2 = Day1Inputs {day1_input: vec![88093, 102524, ***SHORTEN*** 54128, 111698, 84299]};
    let total_fuel_req = day1_2.day1_2();
    (println!("Day 1.2: {:?}", total_fuel_req));
}
Enter fullscreen mode Exit fullscreen mode

Update

I noticed that my repeating the creation of inputs was necessary because I wasn't being smart about ownership. I needed to derive the Clone trait for my new type and then clone it:

// Derive the Clone trait for the new type
#[derive(Clone)]
pub struct Day1Inputs{
    day1_input: Vec<i32>
}

impl Day1Inputs {
   fn day1_1(self) -> i32 {
        let total_fuel_reqs: i32 = self.day1_input.iter().map(|x| ((x / 3) as i32) - 2).sum();
        total_fuel_reqs
        }

    fn day1_2_fuel_calc(mass: i32) -> i32 {
        let new_mass = ((mass / 3) as i32) -2;
        new_mass
    }

    fn day1_2(self) -> i32 {
        let mut total_fuel_mass: i32 = 0;
        for mass in self.day1_input {
            let mut total_fuel_mass_for_object: i32 = 0;
            let mut fuel_req = mass;
            loop {
                fuel_req = Day1Inputs::day1_2_fuel_calc(fuel_req);
                if fuel_req <= 0 {
                    break;
                }
                total_fuel_mass_for_object = total_fuel_mass_for_object + fuel_req;
            }
            total_fuel_mass = total_fuel_mass + total_fuel_mass_for_object;
        }
        total_fuel_mass
    }
}


fn main() {
    let day1 = Day1Inputs {day1_input: vec![88093, 102524, ***SHORTEN*** 54128, 111698, 84299]};
// Add a clone() to this line, moving a clone, not the original
    let fuel_req = day1.clone().day1_1();
    (println!("Day 1.1: {:?}", fuel_req));

// Allowing this line to move the original day1 without errors
    let total_fuel_req = day1.day1_2();
    (println!("Day 1.2: {:?}", total_fuel_req));
}
Enter fullscreen mode Exit fullscreen mode

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay