简体   繁体   中英

Conflicting implementation error with single implementation

In the following program, PartialOrd and PartialEq are implemented for all types that have the trait Area . This way, when defining Rectangle , I only need to implement Area to have < operator working.

trait Area {
    fn get_area(&self) -> i32;
}

impl<T: Area> PartialOrd for T {
    fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
        self.get_area().partial_cmp(&other.get_area())
    }
}

impl<T: Area> PartialEq for T {
    fn eq(&self, other: &T) -> bool {
        self.get_area() == other.get_area()
    }
}

struct Rectangle {
    width: i32,
    height: i32
}

impl Area for Rectangle {
    fn get_area(&self) -> i32 {
        self.width * self.height
    }
}


fn main() {
    let r1 = Rectangle { width:10, height:10 };
    let r2 = Rectangle { width:11, height:9 };
    if r1 > r2 {
        println!("r1 is bigger.")
    } else {
        println!("r2 is bigger.")
    }
}

However, I am getter the following error:

error[E0119]: conflicting implementations of trait `std::cmp::PartialOrd<&_>` for type `&_`:
  --> src/main.rs:23:1
   |
17 | impl<T: Area> PartialOrd<T> for T {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: conflicting implementation in crate `core`:
           - impl<A, B> std::cmp::PartialEq<&B> for &A
             where A: std::cmp::PartialEq<B>, A: ?Sized, B: ?Sized;
   = note: downstream crates may implement trait `Area` for type `&_`

The documentation for E0119 gives an example where the author writes two implementations that overlap. But there is only one implementation is this case. Even the format of the error message is strange, as it starts an enumeration of implementations (with a dash) that has only one entry. What does it mean?

I am also getting this other error:

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
  --> src/main.rs:23:6
   |
23 | impl<T: Area> PartialEq<T> for T {
   |      ^ type parameter `T` must be used as the type parameter for some local type
   |
   = note: only traits defined in the current crate can be implemented for a type parameter

Again, the documentation for E0210 gives examples where the type parameter is not covered by a local type, but T is limited to Area everywhere in this case. What does it mean?

The rust compiler can't guarantee that another crate won't implement Area on a & type. This would cause a conflicting PartialOrd implementation. Even though in this particular example it might be impossible, the compiler can't make that judgement.

You can only implement foreign traits on concrete local types. Constraining a generic type to implement a local trait doesn't guarantee that the local trait can only be implemented on local types. If Area is later implemented on foreign type Foo , that would cause your crate to implement foreign trait PartialOrd on foreign type Foo . The common workaround for this is something like:

struct LocalArea<T: Area> {
    value: T,
}

impl<T: Area, U: Area> PartialEq<LocalArea<T>> for LocalArea<U> {
    ...
}

This syntax of using a local struct that contains a generic T is commonly referred to as the Newtype Pattern .

There is indeed more than one implementation in this case as pointed out in the compiler error message:

impl<A, B> std::cmp::PartialEq<&B> for &A
    where A: std::cmp::PartialEq<B>, A: ?Sized, B: ?Sized;

Your impl is bound to Area. But the core library's implementation is more generic and can cover any &A including those which &A: Area . Basically you shouldn't implement a trait foreign to your crate such as PartialOrd for any type that could be potentially foreign to your crate. Even when you are bounding them to Area they can still be defined in other crates (foreign to yours) using your crate.

It seems to me that you want to say any type which implements Area can/must implement PartialOrd and PartialEq as well and there would be a default implementation of those functions. The following solution forces the implementation of those traits and provides the default functions. Though the implementors would need to select and use the provided functions as an additional local step:

struct Rectangle {
    width: i32,
    height: i32
}

trait Area: PartialOrd+PartialEq {
    fn get_area(&self) -> i32;
    
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.get_area().partial_cmp(&other.get_area())
    }
    
    fn eq(&self, other: &Self) -> bool {
        self.get_area().eq(&other.get_area())
    }
}

impl Area for Rectangle {
    fn get_area(&self) -> i32 {
        self.width * self.height
    }
}

impl PartialOrd for Rectangle {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        <Self as Area>::partial_cmp(self, other)
    }
}

impl PartialEq for Rectangle {
    fn eq(&self, other: &Self) -> bool {
        <Self as Area>::eq(self, other)
    }
}

fn main() {
    let r1 = Rectangle { width:10, height:10 };
    let r2 = Rectangle { width:11, height:9 };
    if r1 > r2 {
        println!("r1 is bigger.")
    } else {
        println!("r2 is bigger.")
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM