I try to implement for educational purposes a simple trait called Compare that implement equal function.
In this example I don't understand why when I have an implementation in the generic trait, the compiler can't use the implementation for i32. When I don't put the implementation in the trait it work fine.
How can I tell the compiler to use the non specialized first then the generic if no specialization is found?
When you change it to explicit form, the compiler can explain why:
Compare::<i32>::equal(&1.0f32, &2i32)
the trait std::cmp::PartialEq<i32> is not implemented for f32
Your trait requires this to be true (and i32 is not comparable to f32 in Rust). In your implementation later you cast to avoid actually using that requirement, but the requirement exists anyway.
There is no such thing here. There is one and only one definition of the trait that always applies everywhere to all its implementations. If the trait requires PartialEq, there is no way around it.
Rust checks validity against declarations, not against implementations.
Personally think it is more a compiler bug in allowing the definition;
fn equal(&self, other: &i32) -> bool {
Trying polymorphic might give some breadcrumbs to follow;
let f = 1.0f32;
let c:&dyn Compare<i32> = &f;
println!(" = {}", c.equal(&2i32));
warning: the trait `Compare` cannot be made into an object
--> src/main.rs:6:8
|
6 | fn equal(&self, other: &T) -> bool
| ^^^^^
|
= note: `#[warn(where_clauses_object_safety)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
= note: method `equal` references the `Self` type in where clauses
error[E0277]: can't compare `dyn Compare<i32>` with `i32`
--> src/main.rs:31:25
|
31 | println!(" = {}", c.equal(&2i32));
| ^^^^^ no implementation for `dyn Compare<i32> == i32`
|
= help: the trait `std::cmp::PartialEq<i32>` is not implemented for `dyn Compare<i32>`
The problem here is that the compiler try to use the equal that required PartialEq but a specialization is made for types it complaining about. It should not take the generic trait impl but the specialized one. Well, this works with the nightly specialization
Your example doesn't use the specialization feature, and the nightly specialization feature is known to have holes.
At least currently on stable, something implements a trait or a trait method if and only if it meets all the requirements in the trait definition. It doesn't matter what the implementation uses or doesn't use, as the trait definition is intentionally enforced even for purely theoretical reasons (so that e.g. your implementation can change in the future and start relying on stuff declared in the trait definition).
The compiler should probably complain about lack of repeated where for the method to avoid giving false impression that it's not there.
In the first example yes. But in the second example ( with specialization feature ) the compiler choose the correct one. It's a lack of the compiler yes, I think.
What is odd, is that :
pub trait Compare<T : ?Sized = Self>
where Self : core::cmp::PartialEq<T>
{
// This not works
fn equal(&self, other: &T) -> bool
{
self == other
}
}
Give the following error:
error[E0277]: can't compare `f32` with `i32`
--> src/main.rs:21:6
|
21 | impl Compare<i32> for f32 {
| ^^^^^^^^^^^^ no implementation for `f32 == i32`
|
= help: the trait `std::cmp::PartialEq<i32>` is not implemented for `f32`
For this, I'm ok with that, the where clause is apply to the trait and all the related functions.
In the initial case the where clause is not apply to the trait but to the function!
So logically the compiler should take the specialized one, if not, it's should complains about the fn equal in generic function and return that std::cmp::PartialEq<i32> is not implemented for f32 at the function level.
It is valid to create bounds that cannot be satisfied, e.g.:
trait Foo where Self: Drop + Copy {}
and it's valid to make an implementation that will never be applicable:
trait Foo {}
impl<T> Foo for T where Self: Drop + Copy {}
It's a type-system equivalent of if false {}.
The implementation with the cast compiled, because it didn't actually rely on the where clause thanks to the cast, so the code of the impl was valid and possible to compile.
In the second case the implementation was invalid itself and tried to perform unsupported operation right there, and there was no way to generate that code.
The difference is between "if pigs fly, make them wear goggles" (we know how to put goggles, so we'll do it when we see a flying pig) vs "make the pig fly" (error, unimplemented).
One question, does equals accept any type different than Self for T? It didn't seem so. Why should f32 of type Comparator<T> be compared with an type T=i32 if the constraint T=Self=f32 holds?