Maths operations on a tuple struct

Hi, I'm trying to use the newtype pattern to avoid bugs and be a better programmer :slight_smile:

I have struct Foo(usize)

I want to be able to %, /, >=, +, - all with other usizes.

Add and Sub, Div and Rem have been easy: (example)

impl std::ops::Add<usize> for Foo {
    type Output = Self;

    fn add(self, other: usize) -> Self::Output {
        Self(self.0 + other)
    }
}

But I'm really struggling with the compare operators. they require a number of previous Traits to also be implemented, and I'm getting lost in the woods a little.

Please help!

Could you show us your Foo structure?

it is a tuple struct, litterally that: struct Foo(usize)

#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
struct Foo(usize);

impl std::ops::Add<Foo> for Foo {
    type Output = Self;

    fn add(self, other: Foo) -> Self::Output {
        Self(self.0 + other.0)
    }
}

impl std::ops::Add<usize> for Foo {
    type Output = Self;

    fn add(self, other: usize) -> Self::Output {
        Self(self.0 + other)
    }
}

impl std::fmt::Display for Foo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}


fn main() {
    let foo1 = Foo(0);
    let foo2 = Foo(15);
    let foo3 = Foo(20);
    let foo4 = Foo(0);
    
    println!("{}, {}, {}", foo1 == foo2, foo2 == foo3, foo1 == foo4);
    println!("{}", foo1 + foo2 + foo3);
    println!("{}", foo1 < foo2);
    println!("{}", foo1 + 10usize);
}
1 Like

Thank you very much, but I want to compare it with a usize, not with another of itself... i.e. if foo > 14 {...}

So you can't use #[derive] in this case as all the ops need to be <usize> for Foo

We can only compare equal types. However, thanks to rust's zero-cost abstractions, you can implement this:

impl From<usize> for Foo {
    fn from(value: usize) -> Self {
        Self(value)
    }
}

Then, call .into() on the usize value:

    println!("{}", foo2 < (10usize).into())

Ah really!? But Foo is a usize! :disappointed_relieved:

What about something like impl Deref for Foo?

1 Like
impl std::ops::Deref for Foo {
    type Target = usize;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

Then, do this:

println!("{}", *foo2 < 10usize)

Good idea. Looks much cleaner

Ah nice, so then, I don't need to impl any maths operations at all now, right. It's now just a straight up usize

1 Like

Well, not only:

use core::cmp::Ordering;
use std::cmp::{PartialOrd, PartialEq};

struct Foo(usize);

impl PartialEq<usize> for Foo {
    fn eq(&self, other: &usize) -> bool {
        self.0.eq(other)
    }
}

impl PartialOrd<usize> for Foo {
    fn partial_cmp(&self, other: &usize) -> Option<Ordering> {
        self.0.partial_cmp(other)
    }
}

fn main() {
    assert!(Foo(1) > 0);
}
2 Likes

Wow, snap!

I just concurrently figured it out myself!

use std::cmp::{PartialOrd, Ordering};

struct Foo(usize);

impl PartialEq<usize> for Foo {
    fn eq(&self, other: &usize) -> bool {
        self.0 == *other
    }
}

impl PartialOrd<usize> for Foo {
    fn partial_cmp(&self, other: &usize) -> Option<Ordering> {
        Some(self.0.cmp(&other))
    } 
}

fn main() {
    dbg!(Foo(0) < 1usize);
}

Freaky

I didn't see that in the docs for PartialOrd in the code section (https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html). They should make it more explicit in the examples. Good to know something new!

1 Like