-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tracking issue for negative impls #68318
Comments
Odd possibly off topic question about this. The following compiles:
Should it? |
I would say no, because if you wanted to shift the error to runtime you could implement Drop as:
I generally like to catch anything at compile time that can be caught at compile time. |
According to the documentation
As a trait bound, (If you did not already know, drop glue is the term for all the code that Yes, the |
Not sure if this is the right place to report a bug with the current nightly implementation, but a negative implementation and its converse seem to "cancel each other out". For example: trait A {}
trait B {}
// logically equivalent negative impls
impl<T: A> !B for T {}
impl<T: B> !A for T {}
// this should not be possible, but compiles:
impl A for () {}
impl B for () {} The above compiles without errors on nightly, though it shouldn't. Removing one of the negative impls fixes the issue and results in an error as expected. |
Allowing negative trait bounds would make my code much better by allowing to express di- and poly-chotomy. In Rust 1.57.0 the following doesn't compile: #![feature(negative_impls)]
trait IntSubset {}
impl <T> IntSubset for T where T: FixedSizeIntSubset + !ArbitrarySizeIntSubset {}
impl <T> IntSubset for T where T: !FixedSizeIntSubset + ArbitrarySizeIntSubset {}
trait FixedSizeIntSubset {}
impl<T: FixedSizeIntSubset> !ArbitrarySizeIntSubset for T {}
trait ArbitrarySizeIntSubset {}
impl<T: ArbitrarySizeIntSubset> !FixedSizeIntSubset for T {} |
Coming here from a compiler error, how do I "use marker types" to indicate a struct is not Send on stable Rust? I don't see any "PhantomUnsend" or similar anywhere in std.
|
@mwerezak I think I've seen
|
@rMazeiks Thanks, I wouldn't have known really what type would be the appropriate choice to use with A |
I think we should just rule out |
Can we add "negative super traits" to the unwritten RFC? That is, pub trait Foo {}
pub trait Bar: !Foo {}
// We now know that the two traits are exclusive, thus can write:
trait X {}
impl<T: Foo> X for T {}
impl<T: Bar> X for T {} Quote from the Hackmd:
Negative super traits should get around this: the above snippet should work even if The potential issue here is that adding implementations to any trait exported by a library is technically a breaking change (though I believe it is already in some circumstances due to method resolution, and also is with any other aspect of negative trait bounds). |
I would prefer to leave that for future work, @dhardy -- I'd rather not open the door on negative where clauses just now, but also I think that it'd be interesting to discuss the best way to model mutually exclusive traits (e.g., maybe we want something that looks more like enums). |
@Bajix This sounds like a hack around lifetimes. Is there any guarantee that accessing a I'm asking because, once specialization is available, it may make sense to let every type implement |
Note that “does not implement
I agree, but I don't think specialization is necessarily required. I propose this at rust-lang/rfcs#3762 (comment) |
Please start a thread for |
Also, FWIW, I'm treating my earlier comment as a list of current questions, so, any team members are free to edit it to update the list if necessary: #68318 (comment) Or just move it to the issue description! |
@rustbot labels -I-lang-nominated In lang triage today we reviewed this and answered the three questions outstanding for us:
We want to see an RFC for this. There is enough to document here that we felt this warranted.
Our feeling was that it was fine to just remove conditional negative impls for now. Though this is somewhat of an implementation question; I doubt we'd object as a lang matter to these staying around under a separate feature gate.
We didn't see this as a hard requirement and felt it was fine to treat this as a future extension. |
Thank you for clarifying everything! This issue should probably be tagged as needs-rfc then, assuming I understand the issue tags correctly. |
I'm working on splitting out negative impls into a separate feature gate ( |
Where? Could you please post a link? |
e.g. https://doc.rust-lang.org/std/boxed/struct.Box.html#impl-Iterator-for-%26Box%3C%5BI%5D,+A%3E (which specializes https://doc.rust-lang.org/std/primitive.str.html#impl-Error-for-%26str (which specializes |
after a quick chat with @compiler-errors I would like to understand the "no conditional impls" restriction outside of disabling auto trait implementations. I do not believe negative impls are the correct feature to disable builtin auto trait impls because negative impls provide an API guarantee which is most likely not what people actually intend to do. Instead of adding #[disable_builtin_impl(Send)]
struct ThisIsNotSend(u32); |
In our lang discussions, I gather that we all agreed that negative impls are not the correct way to disable those impls in general, exactly because of the API guarantee you mention. That's why we are interested in seeing some way to do that exist eventually (other than adding e.g. // A crate is at v1:
trait Tr {}
// An external crate now does:
struct LocalTy;
impl Tr for Rc<LocalTy> {}
// Later, the original crate wants to add:
impl<T> Tr for T {} //~ Breaking change. So, what we may want so that the trait's crate can hold space is: impl<T> ?Tr for T {} //~ Proposed feature. We discussed how you could almost get this with a blanket impl with an alway-ambiguous WC bound, except that you'd have to impl the trait items. @nikomatsakis also mentioned:
|
I clarified with @nikomatsakis and wanted to state that I've decided that imposing the "always applicable" restriction for all negative trait impls is a bit arbitrary, but it is still useful for auto traits, since like positive impls, this probably doesn't mean the thing you want it to mean: struct W<T>(T);
impl !Send for W<i32> {} Namely, that negative impl also opts-out the built-in positive impl of But for non-auto traits, having "partial" negative impls is actually pretty useful. Or at least it makes no sense to restrict the generality of the feature. I'll put up a feature that checks that negative impls of auto traits are always applicable. |
Just for the record-- I'm aligned with this. I don't think that the "always applicable" restriction is necessary in any particular way but I think that for auto traits it's a reasonable place to start.
The "auto trait" model is that you get the "automatic impl" only if you do not provide any impls (pos or neg) of your own. I think that this can be surprising to users. We could explore how to make it less surprising in the future (\*), or *maybe* even tweak those rules (probably not backwards compat, I haven't given it much thought), but for now I think it's sensible to just limit negative impls to the always applicable case to leave ourselves room for more changes.
(\*) One idea I had is a lint for impls of auto traits that warns unless you also tag the struct with something like `#[no_auto_impl(Send)]` etc.
…On Thu, Feb 27, 2025, at 5:40 PM, Michael Goulet wrote:
I clarified with @nikomatsakis <https://github.com/nikomatsakis> and wanted to state that I've decided that imposing the "always applicable" restriction for *all* negative trait impls is a bit arbitrary, but it is still useful for auto traits, since like positive impls, this probably doesn't mean the thing you want it to mean:
struct W<T>(T);
impl !Send for W<i32> {}
Namely, that negative impl also opts-out the built-in positive impl of `Send` for `W<T>` even for types like `W<i32>`. See #93367 <#93367> for why (I believe this is the right issue).
But for non-auto traits, having "partial" negative impls is actually pretty useful. Or at least it makes no sense to restrict the generality of the feature.
I'll put up a feature that checks that negative impls of auto traits are always applicable.
—
Reply to this email directly, view it on GitHub <#68318 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AABF4ZXFKETCCHVXOMH7C7T2R6H4RAVCNFSM4KINDT52U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TENRYHEZDMNJQGMYA>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
compiler-errors*compiler-errors* left a comment (rust-lang/rust#68318) <#68318 (comment)>
I clarified with @nikomatsakis <https://github.com/nikomatsakis> and wanted to state that I've decided that imposing the "always applicable" restriction for *all* negative trait impls is a bit arbitrary, but it is still useful for auto traits, since like positive impls, this probably doesn't mean the thing you want it to mean:
struct W<T>(T);
impl !Send for W<i32> {}
Namely, that negative impl also opts-out the built-in positive impl of `Send` for `W<T>` even for types like `W<i32>`. See #93367 <#93367> for why (I believe this is the right issue).
But for non-auto traits, having "partial" negative impls is actually pretty useful. Or at least it makes no sense to restrict the generality of the feature.
I'll put up a feature that checks that negative impls of auto traits are always applicable.
—
Reply to this email directly, view it on GitHub <#68318 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AABF4ZXFKETCCHVXOMH7C7T2R6H4RAVCNFSM4KINDT52U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TENRYHEZDMNJQGMYA>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
…negative-impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang#68318 (comment)
…negative-impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang#68318 (comment)
…negative-impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang#68318 (comment)
…negative-impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang#68318 (comment)
…negative-impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang#68318 (comment)
…negative-impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang#68318 (comment)
…negative-impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang#68318 (comment)
…negative-impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang#68318 (comment)
Rollup merge of rust-lang#137764 - compiler-errors:always-applicable-negative-impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang#68318 (comment)
…impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang/rust#68318 (comment)
…impl, r=lcnr Ensure that negative auto impls are always applicable r? lcnr (or reassign if you dont want to review) rust-lang/rust#68318 (comment)
Generalized negative impls were introduced in #68004. They were split out from "opt-in builtin traits" (#13231).
Work in progress
This issue was added in advance of #68004 landed so that I could reference it from within the code. It will be closed if we opt not to go this direction. A writeup of the general feature is available in this hackmd, but it will need to be turned into a proper RFC before this can truly advance.
Current plans
!Drop
(negative impls ofDrop
)Unresolved questions to be addressed through design process
default impl !Trait for Type { }
? (Context)The text was updated successfully, but these errors were encountered: