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

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

bretthancox profile image bretthancox Updated on ・3 min read

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);
}

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));
}

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));
}

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));
}

Discussion

pic
Editor guide