#space-age converter with macro_rules

6 messages · Page 1 of 1 (latest)

tulip sphinx
#

Hi, I've just done one of the exercises from exercism: https://exercism.org/tracks/rust/exercises/space-age

Basically, given a time period in seconds, calculate the equivalent number of years for planets other than Earth based on their orbital period.

Since the only thing that really changes with each planet is that single variable, I tried solving it with the help of a macro, and was wondering what I could improve/do differently.

My solution

#[derive(Debug)]
pub struct Duration {
    seconds: u64,
}

impl From<u64> for Duration {
    fn from(s: u64) -> Self {
        Self { seconds: s }
    }
}

pub trait Planet {
    fn orbital_period_in_earth_years() -> f64;
    fn years_during(d: &Duration) -> f64 {
        const DAYS_PER_EARTH_YEAR: f64 = 365.25;
        const SECONDS_PER_DAY: f64 = 60.0 * 60.0 * 24.0;
        const SECONDS_PER_YEAR: f64 = SECONDS_PER_DAY * DAYS_PER_EARTH_YEAR;
        return d.seconds as f64 / SECONDS_PER_YEAR / Self::orbital_period_in_earth_years();
    }
}

macro_rules! planet {
    ($name:ident, $period:expr) => {
        pub struct $name;
        impl $name {}
        impl Planet for $name {
            fn orbital_period_in_earth_years() -> f64 {
                return $period;
            }
        }
    };
}

planet!(Mercury, 0.2408467);
planet!(Venus, 0.61519726);
planet!(Earth, 1.0);
planet!(Mars, 1.8808158);
planet!(Jupiter, 11.862615);
planet!(Saturn, 29.447498);
planet!(Uranus, 84.016846);
planet!(Neptune, 164.79132);

The given test cases:

use space_age::*;

fn assert_in_delta(expected: f64, actual: f64) {
    let diff: f64 = (expected - actual).abs();
    let delta: f64 = 0.01;
    if diff > delta {
        panic!("Your result of {actual} should be within {delta} of the expected result {expected}")
    }
}

#[test]
fn age_on_earth() {
    let seconds = 1_000_000_000;
    let duration = Duration::from(seconds);
    let output = Earth::years_during(&duration);
    let expected = 31.69;
    assert_in_delta(expected, output);
}

#[test]
fn age_on_mercury() {
    let seconds = 2_134_835_688;
    let duration = Duration::from(seconds);
    let output = Mercury::years_during(&duration);
    let expected = 280.88;
    assert_in_delta(expected, output);
}

#[test]
fn age_on_venus() {
    let seconds = 189_839_836;
    let duration = Duration::from(seconds);
    let output = Venus::years_during(&duration);
    let expected = 9.78;
    assert_in_delta(expected, output);
}

#[test]
fn age_on_mars() {
    let seconds = 2_129_871_239;
    let duration = Duration::from(seconds);
    let output = Mars::years_during(&duration);
    let expected = 35.88;
    assert_in_delta(expected, output);
}

#[test]
fn age_on_jupiter() {
    let seconds = 901_876_382;
    let duration = Duration::from(seconds);
    let output = Jupiter::years_during(&duration);
    let expected = 2.41;
    assert_in_delta(expected, output);
}

#[test]
fn age_on_saturn() {
    let seconds = 2_000_000_000;
    let duration = Duration::from(seconds);
    let output = Saturn::years_during(&duration);
    let expected = 2.15;
    assert_in_delta(expected, output);
}

#[test]
fn age_on_uranus() {
    let seconds = 1_210_123_456;
    let duration = Duration::from(seconds);
    let output = Uranus::years_during(&duration);
    let expected = 0.46;
    assert_in_delta(expected, output);
}

#[test]
fn age_on_neptune() {
    let seconds = 1_821_023_456;
    let duration = Duration::from(seconds);
    let output = Neptune::years_during(&duration);
    let expected = 0.35;
    assert_in_delta(expected, output);
}
Exercism

Can you solve Space Age in Rust? Improve your Rust skills with support from our world-class team of mentors.

jagged musk
#

impl $name {} is useless

#

the const stuff i'd personally just put it outside the trait to make it clearer

#

adding a new method is eh if you are going thru the macro route but eh

#

i'd do
||

const DAYS_PER_EARTH_YEAR: f64 = 365.25;
const SECONDS_PER_DAY: f64 = 60.0 * 60.0 * 24.0;
const SECONDS_PER_YEAR: f64 = SECONDS_PER_DAY * DAYS_PER_EARTH_YEAR;

pub trait Planet {
    fn years_during(d: &Duration) -> f64;
}

macro_rules! planet {
    ($name:ident, $period:expr) => {
        pub struct $name;
        impl Planet for $name {
            fn years_during(d: &Duration) -> f64 {
                d.seconds as f64 / SECONDS_PER_YEAR / $period
            }
        }
    };
}

||

tulip sphinx
#

ah of course, I can just use the factor directly in the impl block lol