Tuple with generic fails to compile, why?

use core::fmt::Debug;

#[derive( PartialEq,  Debug, Clone, Copy)]
struct TestMe1<T>
where T:Clone+Copy+Debug
(u8, T);

fn main() {
      println!("hi")
}

Error

   Compiling playground v0.0.1 (/playground)
error[E0658]: parenthetical notation is only stable when used with `Fn`-family traits
 --> src/main.rs:5:20
  |
5 |   where T:Clone+Copy+Debug
  |  ____________________^
6 | | (u8, T);
  | |_______^
  |
  = note: see issue #29625 <https://github.com/rust-lang/rust/issues/29625> for more information

error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied
 --> src/main.rs:5:20
  |
5 |   where T:Clone+Copy+Debug
  |  ____________________^^^^^-
  | |                    |
  | |                    expected 0 generic arguments
6 | | (u8, T);
  | |_______- help: remove these parenthetical generics

error[E0220]: associated type `Output` not found for `Debug`
 --> src/main.rs:5:20
  |
5 |   where T:Clone+Copy+Debug
  |  ____________________^
6 | | (u8, T);
  | |_______^ associated type `Output` not found

Some errors have detailed explanations: E0107, E0220, E0658.
For more information about an error, try `rustc --explain E0107`.
error: could not compile `playground` (bin "playground") due to 3 previous errors
Standard Output

Tuple has to go before the where clause, like this:

use core::fmt::Debug;

#[derive( PartialEq,  Debug, Clone, Copy)]
struct TestMe1<T>(u8, T)
where T:Clone+Copy+Debug;


fn main() {
      println!("hi")
}

You could also save yourself some writing like this:

use core::fmt::Debug;

#[derive( PartialEq,  Debug, Clone, Copy)]
struct TestMe1<T: Debug+Clone+Copy>(u8, T);


fn main() {
      println!("hi")
}

Why does that compile? Doesn't T also need to implement PartialEq in order for it to be derived for the Tuple?

It will be added automatically as a bound for the PartialEq impl. Same for Debug and Clone.

See Derive - The Rust Reference and Implied bounds and perfect derive · baby steps.

There's been some discussion to change this, but it's not going to be soon: #[derive] sometimes uses incorrect bounds · Issue #26925 · rust-lang/rust · GitHub

Trait bounds can appear both in struct definitions and in trait or method implementations. Expanding the derive macro for PartialEq gets us this:

#[automatically_derived]
impl<T: ::core::cmp::PartialEq + Debug + Clone + Copy> ::core::cmp::PartialEq
    for TestMe1<T> {
    #[inline]
    fn eq(&self, other: &TestMe1<T>) -> bool {
        self.0 == other.0 && self.1 == other.1
    }
}

so while we don't require the generic in the struct to implement partial equal at instantiation time, any time we try to compare two values would cause a compilation error:

use core::fmt::Debug;

#[derive( PartialEq,  Debug, Clone, Copy)]
struct TestMe1<T: Debug+Clone+Copy>(u8, T);
#[derive(Debug, Clone, Copy)]
struct NoEqual;
fn main() {
    let val = TestMe1(1, NoEqual);
    let val2 = TestMe1(2, NoEqual);
    let comp = val == val2;      
}
  Compiling playground v0.0.1 (/playground)
error[E0369]: binary operation `==` cannot be applied to type `TestMe1<NoEqual>`
  --> src/main.rs:10:20
   |
10 |     let comp = val == val2;
   |                --- ^^ ---- TestMe1<NoEqual>
   |                |
   |                TestMe1<NoEqual>
   |
note: an implementation of `PartialEq` might be missing for `NoEqual`
  --> src/main.rs:6:1
   |
6  | struct NoEqual;
   | ^^^^^^^^^^^^^^ must implement `PartialEq`
help: consider annotating `NoEqual` with `#[derive(PartialEq)]`
   |
6  + #[derive(PartialEq)]
7  | struct NoEqual;
   |

For more information about this error, try `rustc --explain E0369`.
error: could not compile `playground` (bin "playground") due to previous error

My understanding is that trait bounds are put on methods instead of built into the struct because it is often less verbose to write code this way. You also only have to require traits that actually get used. Imagine you want to use this TestMe struct, but don't care about equality, but you still want to use all the other functionality. You're allowed to use this without implementing PartialEq on your type, as long as you don't try to compare them:

use core::fmt::Debug;

#[derive( PartialEq,  Debug, Clone, Copy)]
struct TestMe1<T: Debug+Clone+Copy>(u8, T);
#[derive(Debug, Clone, Copy)]
struct NoEqual;
fn main() {
    let val = TestMe1(1, NoEqual);
//copy and clone used here
    let val2 = val;
//and now lets try debug
    println!("{val:?} and {val2:?}");
}

This is good! we don't have to write code we won't end up using! This case is trivial, but it can be important when trait implementations become more complex.

Side note: in general you don't want bounds on the generic parameters of your structs. That makes the the bounds become required everywhere. Instead, add bounds where you actually need to utilize them elsewhere.

This

#[derive(PartialEq, Debug, Clone, Copy)]
struct TestMe1<T>(u8, T)
where
    // n.b. `Clone` is a supertrait of `Copy` so `Copy` implies `Clone`
    T: Copy + Debug;

fn doesnt_care_about_copy_or_debug<T>(_: TestMe1<T>)
where
    // But has to add these bounds anyway, or the compiler errors
    T: Copy + Debug,
{
}

fn does_care_and_thus_mentions_bounds<T>(t: TestMe1<T>)
where
    T: Copy + Debug,
{
    let _a_copy = t;
    println!("{t:?}");
}

Versus this

#[derive(PartialEq, Debug, Clone, Copy)]
+struct TestMe1<T>(u8, T);
-struct TestMe1<T>(u8, T)
-where
-    T: Copy + Debug;

 fn doesnt_care_about_copy_or_debug<T>(_: TestMe1<T>)
-where
-    T: Copy + Debug,
 {
 }

 fn does_care_and_thus_mentions_bounds<T>(t: TestMe1<T>)
 where
+    // No added pain since it needed the bounds anyway
     T: Copy + Debug,
 {
     let _a_copy = t;
     println!("{t:?}");
 }

It has other benefits too.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.