Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing PartialEq for a struct with From types

I am trying to implement PartialEq between a struct I created and other types for which my struct implements the From trait. The real code is more complex and implements From for other types, but this is a stripped down version of the core problem.

I want to be able to do:

let s = Data::from(5);
assert_eq!(5, s);

This is the base code:

struct Data {
    data: i64,
}

impl From<i64> for Data {
    fn from(v: i64) -> Data {
        Data { data: v }
    }
}

impl<'a> From<&'a i64> for Data {
    fn from(v: &'a i64) -> Data {
        Data { data: v.clone() }
    }
}

This was my first try:

impl<T> PartialEq<T> for Data
    where T: Into<Data>
{
    fn eq(&self, other: &T) -> bool {
        let o = Data::from(other);
        self.data == o.data
    }
}

but I get an error:

error: the trait bound `Data: std::convert::From<&T>` is not satisfied [--explain E0277]
  --> <anon>:21:17
   |>
21 |>         let o = Data::from(other);
   |>                 ^^^^^^^^^^
help: consider adding a `where Data: std::convert::From<&T>` bound
note: required by `std::convert::From::from`

So I changed the trait bound to what the compiler suggested and added all the requested lifetimes to fix the missing lifetime specifier error:

impl<'a, T> PartialEq<T> for Data
    where T: 'a, Data: From<&'a T>
{
    fn eq(&self, other: &'a T) -> bool {
        let o = Data::from(other);
        self.data == o.data
    }
}

From which I get

error: method not compatible with trait [--explain E0308]
  --> <anon>:31:5
   |>
31 |>     fn eq(&self, other: &'a T) -> bool {
   |>     ^ lifetime mismatch
note: expected type `fn(&Data, &T) -> bool`
note:    found type `fn(&Data, &'a T) -> bool`
note: the anonymous lifetime #2 defined on the block at 31:39...
  --> <anon>:31:40
   |>
31 |>     fn eq(&self, other: &'a T) -> bool {
   |>                                        ^
note: ...does not necessarily outlive the lifetime 'a as defined on the block at 31:39
  --> <anon>:31:40
   |>
31 |>     fn eq(&self, other: &'a T) -> bool {
   |>                                        ^
help: consider using an explicit lifetime parameter as shown: fn eq(&self, other: &'a T) -> bool
  --> <anon>:31:5
   |>
31 |>     fn eq(&self, other: &'a T) -> bool {
   |>     ^

And now I'm lost, as it suggests to do exactly what I did and it refused... :/

Code on the playground

like image 544
Shu Avatar asked Sep 25 '16 08:09

Shu


2 Answers

The compiler is right: adding where Data: From<&T> is the right thing to do. But as you already noticed, a lifetime specifier is required in this case. But how do we declare it?

What we want to say to the compiler:

Data should implement From<&'a T> for any lifetime 'a

We can't declare the lifetime on the impl block, because this expresses something different. We instead need to use “higher-ranked lifetime bounds”, as shown here:

    where Data: for<'a> From<&'a T>
//              ^^^^^^^

This fixes your main problem.


There are two minor, unrelated, additional problems:

  • you need to swap the arguments in assert_eq!(), because of the way PartialEq is used: assert_eq!(s, 5)
  • you need to #[derive(Debug)] for your Data type

You can find a working version here on the playground.

like image 157
Lukas Kalbertodt Avatar answered Nov 09 '22 08:11

Lukas Kalbertodt


You just need one tiny modification to make that PartialEq work: require Data: From<&'a T> since you're using Data::from(other) and not other.into():

impl<T> PartialEq<T> for Data
    where for<'a> Data: From<&'a T>
{
    fn eq(&self, other: &T) -> bool {
        let o = Data::from(other);
        self.data == o.data
    }
}

You also need two tiny modifications to make the assert_eq! work:

  1. Since you're implementing PartialEq for Data, the RHS is T and the LHS is Data, so you can only compare using Data::from(5) == 5 and not 5 == Data::from(5).

  2. You need to implement Debug if you want to use assert_eq!.

Final working code:

#[derive(Debug)]
struct Data {
    data: i64,
}

impl From<i64> for Data {
    fn from(v: i64) -> Data {
        Data { data: v }
    }
}

impl<'a> From<&'a i64> for Data {
    fn from(v: &'a i64) -> Data {
        Data { data: v.clone() }
    }
}

impl<T> PartialEq<T> for Data
    where for<'a> Data: From<&'a T>
{
    fn eq(&self, other: &T) -> bool {
        let o = Data::from(other);
        self.data == o.data
    }
}

fn main() {
    let s = Data::from(5);
    assert_eq!(s, 5);
}
like image 36
Dogbert Avatar answered Nov 09 '22 07:11

Dogbert