exercism.io rust track: Mentoring is awesome

June 8, 2021 – 100 days to offload countdown #92

After finishing the reverse string exercise I tackled the next core exercise: Clock

Implement a clock that handles times without dates.

You should be able to add and subtract minutes to it.

Two clocks that represent the same time should be equal to each other.

After fiddling a bit with correct handling of negative hours and minutes, I had a solution probably after three quarters of an hour - reading up on syntax, installing the rustfmt component for cargo fmt, adding hcorr as the last step.

This is the ugly, fast and passing all tests solution:

use std::fmt;

#[derive(Debug, PartialEq)]
pub struct Clock {
    hours: i32,
    minutes: i32,
}

impl Clock {
    pub fn new(hours: i32, minutes: i32) -> Self {
        let min = if minutes < 0 {
            60 + (minutes % 60)
        } else {
            minutes
        };
        let m = min % 60;
        let hcorr = if minutes < 0 && m != 0 { -1 } else { 0 };
        let hour = hours + (minutes / 60 + hcorr);
        let h = (if hour < 0 { 24 + (hour % 24) } else { hour }) % 24;

        Self {
            hours: h,
            minutes: m,
        }
    }

    pub fn add_minutes(&self, minutes: i32) -> Self {
        Clock::new(self.hours, self.minutes + minutes)
    }
}

impl fmt::Display for Clock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:02}:{:02}", self.hours, self.minutes)
    }
}

It's horrible, all the intermitent variables. I submitted this solution nonetheless - it passed all tests and was finished with respect to that. Then I made my first mentoring experience, completly unexpected. When I first started doing exercism.io stuff, there was peer review. Now your solution is submitted to a mentoring queue, and depending on the language track a human being will take a look at your solution. My experience was uplifting – bobahop starts with congratulations on passing all tests and a bullet-point list of things done well (deriving PartialEq and using format syntax for the zero-padding). The critique of all the ugly code is then formulated very positivly as a hint to possible enhancements to the code, and different approaches using external crates are mentioned, as well as pointers to useful stuff that rust gained as a language or in the library. You can find the critique on my second submission over at exercism.io.

This is my code after applying a couple of the suggestions. A lot cleaner and clearer.

use std::fmt;

const MINUTES_PER_HOUR: i32 = 60;
const HOURS_PER_DAY: i32 = 24;
const MINUTES_PER_DAY: i32 = MINUTES_PER_HOUR * HOURS_PER_DAY;

#[derive(Debug, PartialEq)]
pub struct Clock {
    minutes: i32,
}

impl Clock {
    pub fn new(hours: i32, minutes: i32) -> Self {
        let minutes = hours * MINUTES_PER_HOUR + minutes;

        Self {
            minutes: minutes.rem_euclid(MINUTES_PER_DAY),
        }
    }

    pub fn add_minutes(&self, minutes: i32) -> Self {
        Clock::new(0, self.minutes + minutes)
    }

    pub fn minutes(&self) -> i32 {
        self.minutes.rem_euclid(MINUTES_PER_HOUR)
    }

    pub fn hours(&self) -> i32 {
        self.minutes.div_euclid(MINUTES_PER_HOUR)
    }
}

impl fmt::Display for Clock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{:02}:{:02}",
            self.hours(),
            self.minutes()
        )
    }
}

I look forward to the critique of my next solution, mentoring done like this takes exercism.io to another level. Many, many thanks, indeed.