I also stumbled over this a while back and silly me trusted the compiler hint and tried to implement Step for a custom type as a solution, which obviously failed.
I agree that the stable compiler should not talk about unstable trait bounds and lead the user down a path to nowhere.
Stable rustc gives error messages mentioning unstable traits without any regard for whether they’re stable or not. See these three:
"foo".find(123u8);
Error: u8 does not impl Pattern
error[E0277]: the trait bound `u8: Pattern` is not satisfied
--> src/main.rs:2:16
|
2 | "foo".find(123u8);
| ---- ^^^^^ the trait `FnMut(char)` is not implemented for `u8`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Pattern`:
&'b String
&'b [char; N]
&'b [char]
&'b str
&'c &'b str
[char; N]
char
= note: required for `u8` to implement `Pattern`
note: required by a bound in `core::str::<impl str>::find`
[1u8, 2, 3].join(f64::NAN);
Error: `[u8]` does not impl `Join<_>`
error[E0599]: the method `join` exists for array `[u8; 3]`, but its trait bounds were not satisfied
--> src/main.rs:5:17
|
5 | [1u8, 2, 3].join(f64::NAN);
| ^^^^ method cannot be called on `[u8; 3]` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`[u8]: Join<_>`
unsafe { 2_f32.to_int_unchecked::<String>() };
Error: `f32` is not `FloatToInt<String>`
error[E0277]: the trait bound `f32: FloatToInt<String>` is not satisfied
--> src/main.rs:3:39
|
3 | unsafe { 2_f32.to_int_unchecked::<String>() };
| ^^^^^^ the trait `FloatToInt<String>` is not implemented for `f32`
|
= help: the following other types implement trait `FloatToInt<Int>`:
`f32` implements `FloatToInt<i128>`
`f32` implements `FloatToInt<i16>`
`f32` implements `FloatToInt<i32>`
`f32` implements `FloatToInt<i64>`
`f32` implements `FloatToInt<i8>`
`f32` implements `FloatToInt<isize>`
`f32` implements `FloatToInt<u128>`
`f32` implements `FloatToInt<u16>`
and 4 others
note: required by a bound in `core::f32::<impl f32>::to_int_unchecked`
But I don’t see a problem with these unstable traits being mentioned. You aren’t meant to implement them anyway even if they were stable. The error message gives you something to search in std docs and then click “Implementors” to find what you can use, whether it’s stable or not.
This is an important thing to learn with compiler errors about traits: if the compiler says Type: Trait isn’t satisfied it does not try to imply in any way (in general) that “you should implement Trait for Type”. If will tell you such things also in situations where e.g.
the orphan rules wouldn’t allow you to write the impl to begin with, or
the trait comes with restrictions that make it impossible to implement anyway (e.g. Copy is a common example where people sometimes misinterpret error messages as a hint that “you should implement Copy for Type”), or
the real error is that you passed a different type than you wanted in the first place, or
it would be undesirable to do the impl for other reasons
I do agree with @cod10129 posters that unstable traits can be actually nicer than some other errors; the documented trait impls do serve as easy-to-understand documentation of the “set of types that support the operation”. It’s much less approachable if you encounter language features where something only works for a specific set of types that you’d need to look up deep in the language reference.
I also agree with @jofas that compiler diagnostics can often be improved nonetheless. In the issue he linked, there was an active compiler suggestion to add a bound, which is bad if the trait is unstable. In your playground
error[E0599]: the method `collect` exists for struct `Range<MyIndex>`, but its trait bounds were not satisfied
--> src/lib.rs:13:80
|
3 | struct MyIndex(usize);
| -------------- doesn't satisfy `MyIndex: Step`
...
13 | let direct: Vec<MyIndex> = (MyIndex(start)..MyIndex(stop)) .collect();
| ^^^^^^^ method cannot be called on `Range<MyIndex>` due to unsatisfied trait bounds
|
::: /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/range.rs:80:1
|
80 | pub struct Range<Idx> {
| --------------------- doesn't satisfy `std::ops::Range<MyIndex>: Iterator`
|
= note: the following trait bounds were not satisfied:
`MyIndex: Step`
which is required by `std::ops::Range<MyIndex>: Iterator`
`std::ops::Range<MyIndex>: Iterator`
which is required by `&mut std::ops::Range<MyIndex>: Iterator`
note: the trait `Step` must be implemented
--> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/range.rs:24:1
|
24 | pub trait Step: Clone + PartialOrd + Sized {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I find the section “the trait Step must be implemented” has suboptimal wording, because it does sound a bit too much like “you should implement the trait Step for MyIndex” (or maybe that’s even what the wording is meant to say? I’m not even sure…)
error[E0277]: the `?` operator can only be applied to values that implement `Try`
--> src/main.rs:4:9
|
4 | ()?;
| ^^^ the `?` operator cannot be applied to type `()`
|
= help: the trait `Try` is not implemented for `()`
And in both the scenarios of "you need to add a bound" and "you need to add an implementation", it's also good to be aware that the compiler (currently) prefers to highlight a trait that would cause an existing blanket implementation to apply over the direct requirement. (This issue.)
For example:
error[E0277]: the trait bound `T: Clone` is not satisfied
--> src/lib.rs:1:22
|
1 | fn foo<T: ?Sized>(_: std::borrow::Cow<T>) {}
| ^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `T`
|
= note: required for `T` to implement `ToOwned`
note: required by a bound in `Cow`
--> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/borrow.rs:181:8
|
179 | pub enum Cow<'a, B: ?Sized + 'a>
| --- required by a bound in this enum
180 | where
181 | B: ToOwned,
| ^^^^^^^ required by this bound in `Cow`
help: consider further restricting this bound
|
1 | fn foo<T: ?Sized + std::clone::Clone>(_: std::borrow::Cow<T>) {}
| +++++++++++++++++++