Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The compiler reports conflicting even no real conflicting #22865

Closed
laijs opened this issue Feb 27, 2015 · 2 comments
Closed

The compiler reports conflicting even no real conflicting #22865

laijs opened this issue Feb 27, 2015 · 2 comments
Labels
A-trait-system Area: Trait system

Comments

@laijs
Copy link

laijs commented Feb 27, 2015

trait Square { fn length(&self) -> f64; }
trait Circle { fn radius(&self) -> f64; }

// no type parameter, so &Area can cover both Square and Circle
trait Area { fn area(&self) -> f64; }

impl<T:Square> Area for T {
    fn area(&self) -> f64 { self.length() * self.length() }
}

impl<T:Circle> Area for T {
    fn area(&self) -> f64 { std::f64::consts::PI * self.radius() * self.radius() }
}

When compiling, it gives:

rustc --crate-type=rlib square_circle.rs
square_circle.rs:6:1: 8:2 error: conflicting implementations for trait `Area` [E0119]
square_circle.rs:6 impl<T:Square> Area for T {
square_circle.rs:7     fn area(&self) -> f64 { self.length() * self.length() }
square_circle.rs:8 }
square_circle.rs:10:1: 12:2 note: note conflicting implementation here
square_circle.rs:10 impl<T:Circle> Area for T {
square_circle.rs:11     fn area(&self) -> f64 { std::f64::consts::PI * self.radius() * self.radius() }
square_circle.rs:12 }

Actually, the user must impl only one of the Square/Circle to a type, there is no conflicting. But it seems that the compiler reports the conflicting too early. I think the compiler should report until the conflicting is really happened (the user impl the both traits to a type). Workaround for it is simple, but the above code is simpler and more readablity than workarounds.

@kmcallister kmcallister added the A-trait-system Area: Trait system label Mar 1, 2015
@shepmaster
Copy link
Member

Actually, the user must impl only one of the Square/Circle to a type, there is no conflicting

Nothing in the types prevents this, so it's possible for a conflict to arise though - there's nothing that prevents your library or any code that uses your library from doing this:

struct Point;

impl Square for Point {
    fn length(&self) -> f64 { 0.0 }
}

impl Circle for Point {
    fn radius(&self) -> f64 { 0.0 }
}

Now the implementation of Area is ambiguous.

@laijs laijs closed this as completed Mar 5, 2015
@p-avital
Copy link

Nothing in the types prevents this, so it's possible for a conflict to arise though - there's nothing that prevents your library or any code that uses your library from doing this:

Well, there's conflicting implementation detection, isn't there?
Why should we prevent a library from building if it only MIGHT cause conflicting implementations?
Shouldn't we rather prevent the code that uses the library in ways that DO cause conflicting implementations from compiling?
In most cases, code that would cause conflicts is usually also the code that may have done questionable choices, or even choices that we as library developers would like to forbid, but since negative reasoning is forbidden in Rust, we can't.

For example, a Point is neither a Circle nor a Square: it is shapeless (if I asked you to "expand" a point, where "expand" is an additive operation that retains shape but increases some parameter of the shape by a finite value, you would need to cast the Point into a shape before expanding that shape (and the casting shape will, disregarding special cases such as square-matrix displays being the context of the point, be a Circle)), so if you need to model a Point as both, you may need to review why such use-cases happen, and maybe use std::convert to allow Points to become 0-radius Circles or 0-length Squares and back?

The example taken in this issue (which has had many like it appear) really shows (in my opinion) why Rust should switch from preventing POTENTIAL conflicts to preventing ACTUAL conflicts. Why should POTENTIAL conflicts prevent useful things from being done? Sure, warnings would be nice, but errors?

In general, I am against that "Additions should never be breaking" idea, yes it's not the most intuitive thing to debug that your program doesn't build with new stuff that's only been additive, but the error messages are helpful enough that it's easy to understand where and why a conflict occurs, whereas thinking your whole architecture around potential conflicts is really hard and limiting (at least, until we get proper specialization).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-trait-system Area: Trait system
Projects
None yet
Development

No branches or pull requests

4 participants