Description
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 System
s 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!