error[E0277]: the trait bound Node<T>: std::cmp::PartialEq is not satisfied
--> src/main.rs:3:10
|
3 | #[derive(Eq)]
| ^^ can't compare Node<T> with Node<T>
|
= help: the trait std::cmp::PartialEq is not implemented for Node<T>
= note: required by std::cmp::Eq
I'm a little confused about your question... are you asking how you can get Node<T> to implement Eq when the underlying T doesn't implement PartialEq?
This isn't really possible because in order to do a useful comparison for some Node<T> you'll eventually need to compare the inner data element, which isn't possible when T doesn't implement PartialEq.
It might help a bit more with an example. Given a type which looks something like the following
#[derive(Eq)]
struct Node<A, B> {
a: A,
b: B,
}
The #[derive(Eq)] part will expand (roughly) to the following:
This property cannot be checked by the compiler, and therefore Eq implies PartialEq, and has no extra methods.
So, basically the following code compiles:
struct Node<T> {
data: T,
}
// This is all `#[derive(Eq)]` does.
impl<T> Eq for Node<T> {}
// This is you telling what function to call on `==` binary operator
impl<T> PartialEq for Node<T> {
fn eq(&self, other: &Node<T>) -> bool {
false
}
}
but is considered incorrect because for some (and all) Node a, you'll have:
assert_eq!(a == a, false);
Therefore, you have a code that compiles, but is incorrect. Eq operator overloading is one of those cases that usually needs some (or at least just one) unit testing, if the implementation is not obvious.
You can have types that implement both Eq and Default, but Rust has no batteries for testing if the default value provided for the type equals to itself. I find it a bit surprising, but maybe it's just me.
It is worth noting however that Eq represents an equivalence relation which places extra restrictions on the implementation of PartialEq which the compiler can't reason about. So if you really need Eq I'd recommend this amendment instead:
your example is good but did not solve my problem.
consider a scenario: if a type T does not impl PartialEq, so Node<T> does not impl PartialEq neigher, so this Node<T> can not inherit Eq, the compiler should refuse this code. but the compiler accepted it.
Uh, I think now I get what your question is: Why compiler allows having self.data == other.data when you have not explicitly claimed that <T: PartialEq>?
So, I think this goes to Rust's type inference: the compiler figured it out that T needs PartialEq, and everything's fine unless you try to do break this (implicit) expectation.
To trigger an error and see the inference in action, try instantiating Node with a T that doesn't impl PartialEq.
Does this answer your question?
(PS. I could be wrong about how the inference is in work here. So, please let me know if I got it wrong and that's not what's happening.)
Ah, actually it's simple: you previously only implemented PartialEq for Nodes whose inner data type T implements PartialEq. As a result, if you tried to instantiate a Node with a data type which does not implement PartialEq, it would compile, but you would get a Node type which does not implement PartialEq.
If your goal is to state that a contents of a Node must implement PartialEq, then you need to specify this requirement in the struct declaration (as shown earlier in this thread), not in the impl block. The logic behind this is that all impl blocks should be understood as optional in Rust.
I discovered the possibility to optionally implement some functions for a generic type depending on its bounds while I was looking at the source code of the "atomic" crate.
I'm not sure if this is directly documented anywhere at the moment, the only reference I found while looking it up was an addition to the old Rust book which discussed the possibility of writing multiple "impl" blocks for a given type.
You are right that this is a pretty powerful functionality which violates the naive intuition that many of us have regarding "impl" while learning Rust, and that it should probably be more clearly documented. I will check the details of what the new Rust book currently says about this, and propose some additions if relevant.