Why is `rustc_on_unimplemented` only allowed inside std?

I recently found out about the on_unimplemented feature in an older version of the rust documentation.

Sadly, this no longer works with the newest rust-version.

So, I did a bit of digging and found here:

This isn't intended to ever be stabilised (in its current form) and any work towards making it user-definable would require an RFC. I think it would be appropriate to close this issue, and if people want such a feature in non-rustc crates, a new feature request issue could be opened.

So, I wanted to open "a new feature request issue". Here, it says "Please discuss language feature requests on the internals forum."

That's why I'm here today. I want to use rustc_on_unimplemented is user code. It's used in the std library, so we know that the feature still exists and is not broken.

The argument against using it in user code is that "it's not intended to be stable".

But it does't need to be stable.

If you decide to remove the rustc_on_unimplemented, you can just ignore all occurrences of it. This wouldn't contradict rust's stability claims, because it only affects compiler error messages, which aren't stable in the first place. If your code compiled before such a change, it will still compile afterwards and do the same thing.

Similary, if you decide to change the syntax of what comes rustc_on_unimplemented (let's say for example you want to replace

#[rustc_on_unimplemented(
    message="message",
    label="label",
    note="note"
)]

with

#[rustc_on_unimplemented(
    err_message="message",
    label_of_this="label",
    optional_note="note"
)]

all you need to do, is ignore all occurences of rustc_on_unimplemented in user code that have invalid syntax (maybe warn).

TLDR: The way that I see it, rustc_on_unimplemented is a perfectly fine and useful feature that is locked away. Stability concers are invalid, because it only affects compiler error messages.

5 Likes

There was a recent thread regarding this: Better user-definable error messages

1 Like

Are these things that you plan to never implement, and are willing to promise that?

I'm hoping that one day https://github.com/rust-lang/rust/issues/68318 will be integrated into error messages in some way -- perhaps (part of?) the #[doc] comment from the negative impl could be shown in the error message.

2 Likes

What do you mean? If you choose to implement a trait later on, then the message of message="message" will never be printed.

I think the point is that it is a breaking change to remove or loosen the restrictions of a negative impl.

Adding or removing rustc_on_unimplemented does not change the restrictions of a negative impl. rustc_on_unimplemented has no effect on whether it compiles or not.

@scottmcm was suggesting something like:

trait Copy {}

impl Copy for usize {}

/// Strings cannot be trivially copied.
impl !Copy for String {}

fn user_code() {
    // ERROR: Strings cannot be trivially copied
    let x: impl Copy = String::new();
}

For std to add a custom message for String: !Copy errors, it would have to commit to never adding that impl, as that would be a breaking change. This is different than an #[on_unimplemented...] annotation.

1 Like

I know what you mean, but I don't know why he started talking about negative impls. My first post was about rustc_on_unimplemented, this has nothing to do with negative impls.

Because it's a feature that's in the pipeline already that may address your scenario, depending on exactly what that scenario is.

And I think it's structurally a better answer than rustc_on_unimplemented, because adding more and more things to an attribute on the trait definition gets to be more of a mess than might be desirable:

I've kind of wanted custom diagnostics for lacking a trait impl myself.

For example, the Clock trait here, in my rust-chrono library (attempting to partially implement C++'s std::chrono in rust), as well as it's unsafe marker subtraits Monotonic and TrivialClock. Rather than "C doesn't implement Monotonic", I would rather a nicer error message like "C doesn't guarantee monotonicity", or for TrivialClock, "Operations on C are not trivial".

I'd also find it nice for the implementation of abi-safe trait objects in https://github.com/LightningCreations/lccc/blob/riir/xlang/xlang_abi/src/traits.rs. Calling DynMut<dyn Foo>::unsize_mut(), when dyn Trait can't unsize T, should be "Cannot unsize T into dyn Foo in an abi-safe context" rather than "dyn Foo doesn't implement AbiSafeUnsize<T>", the latter of which is a useless error to downstream users, which typically won't be able to impl AbiSafeUnsize<dyn Foo> for T as the impl would typically be provided by the crate that defines dyn Foo and opts it into abi-safe usage.

Even if the negative impl stuff gets approved, rustc_on_unimplemented would still be very useful (for example when the type is not in scope).

Sure, it might get messy, but that's the problem/responsibilty of the library using rustc_on_unimplemented.

The ROI of enabling rustc_on_unimplemented is very good.

The problem with this line of reasoning is that rustc considers regressing diagnostic quality as a stability thing as well. While you can be technically correct in saying that for correct code, the compiler's behavior is consistent, the primary job of a compiler is not to compile correct code, but to diagnose incorrect code.

From a language specification stability point of view, it's okay to say that #[on_unimplemented(...)] is always allowed, and MAY cause or adjust warnings in an implementation-defined way. But for the stability of rustc, rustc should continue to support existing #[on_unimplemented(...)] uses.

Makes sense.

If you want the error messages to be stable you cannot only allow rustc_on_unimplemented outside of std.

That being said, error messages aren't stable in rustc, they frequently change.