Skip to content

Identify which tuple item fails to implement a trait when using fake variadics #141258

Open
@alice-i-cecile

Description

@alice-i-cecile

Code

trait WorksOnDefault {
    fn do_something() {}
}

impl<T: Default> WorksOnDefault for T {}

fn main() {
    let _success = <(i32, u32, String)>::do_something();
    let _failure = <(i32, &u32, String)>::do_something();
}

Current output

error[E0599]: the function or associated item `do_something` exists for tuple `(i32, &u32, String)`, but its trait bounds were not satisfied
  --> examples/hello_world.rs:11:28
   |
11 |     <(i32, &u32, String)>::do_something();
   |                            ^^^^^^^^^^^^ function or associated item cannot be called on `(i32, &u32, String)` due to unsatisfied trait bounds
   |
note: the following trait bounds were not satisfied:
      `&(i32, &u32, String): Default`
      `&mut (i32, &u32, String): Default`
      `(i32, &u32, String): Default`

Desired output

No method `works_on_default` found for <(i32, &u32, String)>`

The method `do_something` is available via the `WorksOnDefault` trait for any type T where T implements the `Default` trait.

If every member of the tuple (i32, &u32, String) implemented the `Default` trait, the `Default` trait would be implemented for the tuple.

However, `&u32` does not implement the `Default` trait.

note: did you mean `u32`, which implements the `Default` trait?

Rationale and extra context

This is an extremely common pattern for Bevy, primarily in our system and bundle code patterns. The poor error messages are one of our largest UX / teaching problems,. See #89681 for that specific issue, which could be fixed by more general improvements here.

This pattern occurs all over the place: the minimal example here uses Default, which relies on the same flavor of fake variadics for tuples where all tuple elements implement a given trait. Bevy's Bundle tuples matches this form exactly, while our Systems use the same sort of trick but with functions, where each parameter must implement SystemParam.

Other cases

fn main(){
    let _successful_default = <(i32, u32, String)>::default();
    let _failed_default = <(i32, &u32, String)>::default();
}

The error output when calling methods from the trait directly correctly identifies the problematic variant:

error[E0599]: the function or associated item `default` exists for tuple `(i32, &u32, String)`, but its trait bounds were not satisfied
 --> src/main.rs:3:50
  |
3 |     let _failed_default = <(i32, &u32, String)>::default();
  |                                                  ^^^^^^^ function or associated item cannot be called on `(i32, &u32, String)` due to unsatisfied trait bounds
  |
  = note: the following trait bounds were not satisfied:
          `&u32: Default`
          which is required by `(i32, &u32, String): Default`
``

Rust Version

rustc 1.87.0 (17067e9ac 2025-05-09)
binary: rustc
commit-hash: 17067e9ac6d7ecb70e50f92c1944e545188d2359
commit-date: 2025-05-09
host: x86_64-unknown-linux-gnu
release: 1.87.0
LLVM version: 20.1.1

Anything else?

Discussed with @estebank and @joshtriplett at RustWeek 2025, where I was encouraged to file an issue so we can improve diagnostics without waiting on full variadics!

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions