Compiler Bug or by design?

struct D(usize);

#[derive(Debug, PartialEq)]
struct Cfg<T> {
    Addr: u8,
    Opt: E<T>
}

enum E<T> {
    A(u8),
    B(T),
}

impl<T> PartialEq for E<T> {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::A(l0), Self::A(r0)) => l0 == r0,
            (Self::B(_), Self::B(_)) => true,
            _ => false,
        }
    }
}

impl<T> std::fmt::Debug for E<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::A(arg0) => f.debug_tuple("A").field(arg0).finish(),
            Self::B(_) => f.debug_tuple("B").finish(),
        }
    }
}

fn main() {
    // This works
    assert_eq!(E::B(D(10)), E::A(1));
    // This not compile
    // assert_eq!(Cfg { Addr: 0, Opt: E::B(D(10))}, Cfg { Addr: 0, Opt: E::B(D(2))});
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
warning: field `0` is never read
 --> src/main.rs:1:10
  |
1 | struct D(usize);
  |        - ^^^^^
  |        |
  |        field in this struct
  |
  = help: consider removing this field
  = note: `#[warn(dead_code)]` on by default

warning: struct `Cfg` is never constructed
 --> src/main.rs:4:8
  |
4 | struct Cfg<T> {
  |        ^^^

warning: structure field `Addr` should have a snake case name
 --> src/main.rs:5:5
  |
5 |     Addr: u8,
  |     ^^^^ help: convert the identifier to snake case: `addr`
  |
  = note: `#[warn(non_snake_case)]` on by default

warning: structure field `Opt` should have a snake case name
 --> src/main.rs:6:5
  |
6 |     Opt: E<T>
  |     ^^^ help: convert the identifier to snake case (notice the capitalization): `opt`

warning: `playground` (bin "playground") generated 4 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.50s
     Running `target/debug/playground`

thread 'main' panicked at src/main.rs:35:5:
assertion `left == right` failed
  left: B
 right: A(1)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I came a cross this issue in real code today.

When using E<T> T doesn't have Debug or PartialEq.
So I give E the manual implementation and don't care about the T.
But if I use E in a other struct Cfg and derive Debug and PartialEq the compiler complains that T doesn't have that implementation.
Which is true.

But what I except is that derive is using the implementation of E.

Is this normal behaviour or a bug?

1 Like

That's the expected behavior.

You can confirm that by expanding macros from top right button on playground webpage to see

#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for Cfg<T> {

#[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for Cfg<T> {

Generally, deriving builtin traits will require the same trait bounds on generics.
That's a choice of compiler, and it's more common for most usecases.

But you can always opt out by implementing them on your own without generics carrying trait bounds. Rust Playground

4 Likes

what you are describing is called "perfect deriving", and lack of the perfect derive feature is a well known limitation of current rust. see:

there's a crate called perfect-derive, which support several standard traits like Debug, Eq, Default, etc.

4 Likes

Ah Yes! Thanks for showing me what is happening under the hood.
Now I see and understand why the compiler is complaining.

@nerditation Thanks for the blog.