Ord, PartialOrd, Eq, PartialEq, Confusion, TotalConfusion


#1

I have a type that represents distances:

#[derive(Clone, Debug, PartialEq)]
enum MeasurementSystem {
    Imperial,
    Metric
}

#[derive(Clone, Debug)]
pub struct Distance {
    units: f64,
    system: MeasurementSystem
}

In my code, i’d like for Distance structs representing 1 meter and its equivalent in feet to equal. I’d also like to iterate over lists of items with distances, sorting them by distance.

In looking at comparison structs, I see Eq, PartialEq, Ord and PartialOrd. What would be the least I’d need to implement to get this working?

Assume the presence of a to method on Distance which mutably converts it to the specified system. There’s probably a more efficient way to do this, but for now I’m cloning distances, converting them to metric, and checking them that way. Here’s my current PartialEq implementation:

impl PartialEq for Distance {
    fn eq(&self, other: &Distance) -> bool {
        use MeasurementSystem::*;
        let mut d1 = self.clone();
        d1.to(Metric);
        let mut d2 = other.clone();
        d2.to(Metric);
        d1.units == d2.units
    }
}```

But I don't know if that is needed, or if I could do something similar with PartialOrd, like so:

impl PartialOrd for Distance {
fn partial_cmp(&self, other: &Distance) -> Option {
use MeasurementSystem::*;
let mut d1 = self.clone();
d1.to(Metric);
let mut d2 = other.clone();
d2.to(Metric);
d1.units.partial_cmp(&d2.units)
}
}


Thanks.

#2

Have you tried taking inspiration from the units crate?
This helps with extactly this usecase.

Its trick is to have different types/impls for each measurement system, so you can let the typesystem figure out which comparison to use, instead of doing it dynamically.

After all, the defining feature of Rust is to do as much as possible statically.


#3

The crate is almost 3 years old. Does it still comply with current rust development?


#4

Thats why I said “inspiration” :slight_smile:
The idea to use a single Type per measurement system, instead of a single type with runtime disambiguation, as OP currently has, should really simplify things, because it makes the conversions more explicit.

Right now, a Distance can be anything (Imperial, Metric, Quatloo’s), and that makes the single Distance-vs-Distance comparator complicated, especially because it has to do runtime disambiguation based on the Distance.system field.

If we expose the MeasurementSystem to Rust’s compiler, as MetricDistrance, ImperialDistance, MartianDistance types, this allows you to define (far simpler) different Eq Impls for the different combinations.


#5

https://ferrisellis.com/posts/rust-implementing-units-for-types/ is a good blog on doing something similar and can be used for inspiration as well.


#6

That’s nice, thanks, I’ll look into it! I was actually thinking of
typing things more strictly later, but figured I’d introduce that more
gradually.


#7

Is there a process for people to declare a crate stalled such that new people can take it over rather than creating a new package based on it? Is it just using the GitHub repository to get the original author to pass it on?


#8

More recent and more full-featured crates are uom and dimensioned.


#9

I can’t seem to find uon, do you have a link? Thanks!


#10

That’s an M not an N :slight_smile: it’s at https://crates.io/crates/uom


#11

Ah, text-to-speech fail, Ns and Ms sound a lot alike at high speeds. :slight_smile:
Thanks for catching that.


#12

Thanks! I didn’t know about those!

For completeness sake, also the link to dimensioned:

https://crates.io/crates/dimensioned


#13

See also https://crates.io/crates/measurements, which I maintain.