简体   繁体   中英

How to work around conflicts of generic implementations of traits (blanket implementations)?

I'm coming from C++ and I'm running into issues with generic trait implemntations.

I'm trying to build a geometry library that does some operation on both polygons and open paths, but behaves slightly different for each case. Ideally I want one implementation of a geometry operation as a method which can query the object it's operating on for a flag.

I also want this library to work on any object that can look like a polygon or open path, not just my data types, so I want traits for PolygonLike and OpenPathLike that can be implemented on any type. I have code that looks like this:

trait Clipable {
    fn closed() -> bool;
    fn geom_func(&self) {
        if Self::closed() {
            println!("closed");
        } else {
            println!("open");
        }
    }
}

trait PolygonLike {}
trait OpenPathLike {}

struct Point(f64, f64);
type PointVec = Vec<Point>;
struct Poly(PointVec);
struct Path(PointVec);

impl PolygonLike for Poly {}
impl OpenPathLike for PointVec {}

impl<T> Clipable for T
where
    T: PolygonLike,
{
    fn closed() -> bool {
        true
    }
}

impl<T> Clipable for T
where
    T: OpenPathLike,
{
    fn closed() -> bool {
        false
    }
}

fn main() {
    let pg = Poly(PointVec::new());
    let op = Path(PointVec::new());
    pg.geom_func();
    op.geom_func();
}

The code fails to compile with:

error[E0119]: conflicting implementations of trait `Clipable`:
  --> src/main.rs:28:1
   |
24 | impl<T> Clipable for T where T: PolygonLike {
   | ------------------------------------------- first implementation here
...
28 | impl<T> Clipable for T where T: OpenPathLike {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation

I have read elsewhere that the where clause isn't used by the conflict checker, even though no type implements both PolygonLike and OpenPathLike .

I get that this is because a consumer could just implement both those traits and break the library. What I don't get is how I can work around this.

I have seen other posts where people have used newtypes to limit generic implementations, but I can't figure out how to make that work for my scenario.

This is currently not possible. You will need to implement Clipable for Poly , PointVec , and any others, individually.

You might consider writing macros to make this less repetitive:

macro_rules! clipable_poly {
    ($($ty: ty),+) => {
        $(
            impl PolygonLike for $ty {}

            impl Clipable for $ty {
                fn closed() -> bool {
                    true
                }
            }
        )+
    }   
}

macro_rules! clipable_open {
    ($($ty: ty),+) => {
        $(
            impl OpenPathLike for $ty {}

            impl Clipable for $ty {
                fn closed() -> bool {
                    false
                }
            }
        )+
    }   
}

clipable_poly!(Poly, Point);
clipable_open!(PointVec, Path);

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