When creating a struct which stores a DST (e.g., a raw slice), I can use the normal #[derive(Eq, PartialEq, Ord, PartialOrd)]
facility to get implementations of this trait on my type:
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct A([u8]);
However, if I implement them manually, then the compiler will complain that my type does not implement Sized
:
struct A([u8]);
impl AsRef<[u8]> for A {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<S: AsRef<[u8]>> PartialEq<S> for A {
fn eq(&self, other: &S) -> bool {
self.0.eq(other.as_ref())
}
}
impl Eq for A { }
impl<S: AsRef<[u8]>> PartialOrd<S> for A {
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
let slice: &[u8] = &self.0;
slice.partial_cmp(other.as_ref())
}
}
impl Ord for A {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(&other).unwrap()
}
}
Compiler result:
rustc 1.12.0 (3191fbae9 2016-09-23)
error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied
--> <anon>:20:6
|
20 | impl Eq for A { }
| ^^
|
= note: `[u8]` does not have a constant size known at compile-time
= note: required because it appears within the type `A`
= note: required because of the requirements on the impl of `std::cmp::PartialEq` for `A`
= note: required by `std::cmp::Eq`
error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied
--> <anon>:29:6
|
29 | impl Ord for A {
| ^^^
|
= note: `[u8]` does not have a constant size known at compile-time
= note: required because it appears within the type `A`
= note: required because of the requirements on the impl of `std::cmp::PartialOrd` for `A`
= note: required by `std::cmp::Ord`
If I create a variant of the type which does have a fixed size (e.g. by turning it into a fixed-size array), then I can manually implement the traits no problem.
struct B([u8; 5]);
impl AsRef<[u8]> for B {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<S: AsRef<[u8]>> PartialEq<S> for B {
fn eq(&self, other: &S) -> bool {
self.0.eq(other.as_ref())
}
}
impl Eq for B { }
impl<S: AsRef<[u8]>> PartialOrd<S> for B {
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
let slice: &[u8] = &self.0;
slice.partial_cmp(other.as_ref())
}
}
impl Ord for B {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(&other).unwrap()
}
}
Here is a playground link showing the issue.
My question is - how do I implement Ord
and Eq
on my custom DST, so that I can take advantage of the fact that I can partial_cmp/eq
by any AsRef<[u8]>
, but also use that to enforce the bounds for the Ord
/Eq
trait implementations, as I do with struct B
in my example?
The problem is that S: AsRef<[u8]>
is also restricting S
to Sized
types by default. You need to use ?Sized
to opt-out of that.
All generic type parameters implicitly have the
Sized
bound, so the?Sized
can be used to opt-out of the implicit bound.
From the Rust Book.
The following compiles fine for me:
use std::cmp::Ordering;
struct A([u8]);
impl AsRef<[u8]> for A {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<S: AsRef<[u8]> + ?Sized> PartialEq<S> for A {
fn eq(&self, other: &S) -> bool {
self.0.eq(other.as_ref())
}
}
impl Eq for A {}
impl<S: AsRef<[u8]> + ?Sized> PartialOrd<S> for A {
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
let slice: &[u8] = &self.0;
slice.partial_cmp(other.as_ref())
}
}
impl Ord for A {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(&other).unwrap()
}
}
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With