PartialEq with Into/From?


#1

Hello! I have an enum and I want to implement PartialEq for it and for the base type of one of the enums variants. The enum and the impl looks like this:

enum Similarity {
    Any(f32),
    Certain
}

impl Similarity {
    pub fn is_certain(&self) -> bool {
        match *self {
            Similarity::Certain => true,
            Similarity::Any(sim) => (sim - 1.0).abs() < f32::EPSILON
        }
    }
    
    pub fn in_percent(&self) -> f32 {
        match *self {
            Similarity::Certain => 1.0,
            Similarity::Any(sim) => sim,
        }
    }
}

Now I want to implement PartialEq for f32 and Similarity, so that I can write both,
println!("{}, {}, {}", Similarity::Certain < Similarity::Any(0.32), Similarity::Certain < 0.32, Similarity::Any(0.32) < 0.22);
and
println!("{}, {}, {}", Similarity::Any(0.32) < Similarity::Certain, 0.32 < Similarity::Certain, 0.22 < Similarity::Any(0.32));

So, normally I would have to implement

  • impl PartialEq for Similarity
  • impl PartialOrd for Similarity
  • impl PartialEq<f32> for Similarity
  • impl PartialOrd<f32> for Similarity
  • impl PartialEq<Similarity> for f32
  • impl PartialOrd<Similarity> for f32

But that is awfully lot of boilerplate code, since the code is nearly identical. So, I’m asking myself: Is there any way to use a generic way? E.g. Use Into / From ?

What I want to archieve is something like this (which does not work):

impl From<f32> for Similarity {
    fn from(sim: f32) -> Self {
        let sim = Similarity::Any(sim);
        if sim.is_certain() {
            Similarity::Certain
        } else {
            sim
        }
    }
}

impl Into<f32> for Similarity {
    fn into(self) -> f32 {
        match self {
            Similarity::Certain => 1.0,
            Similarity::Any(sim) => sim,
        }
    }
}

impl PartialEq<Into<f32>> for Similarity {
    fn eq(&self, other: &Into<f32>) -> bool {
        (self.in_percent() - other.into()).abs() < f32::EPSILON
    }

    fn ne(&self, other: &Into<f32>) -> bool {
        (self.in_percent() - other.into()).abs() > f32::EPSILON
    }
}

Thanks for any advice!


#2

Looks to me like your type should be From<f32> but not Into<f32>. Your From<f32> implementation for your type gives you an Into<Similarity> for f32, so I’d make your equality test use Into<Similarity>.


#3

This case gets, surprisingly, a bit technical. If you don’t want to require that Similarity implements Clone, then here’s one version: playground

If you’re willing to add a Clone bound, it’s slightly less boilerplatey: playground

I can’t say either one is as concise as one might initially hope for. Maybe someone can provide a more terse version.

I’ll also say that you can often mitigate boilerplate with macros, so all’s not lost if a fully generic version isn’t feasible.