Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upAutomatically derive {Clone,PartialEq,PartialOrd} when {Copy,Eq,Ord} are derived #1028
Conversation
This comment has been minimized.
This comment has been minimized.
|
I'm a fan. This feels like an annoying papercut we should fix. |
This comment has been minimized.
This comment has been minimized.
erickt
commented
Apr 1, 2015
|
Thanks for writing this up! One thing to note is that as of right now generic types like impl<T: Clone> Clone for Option<T> {
fn clone(&self) -> Self { *self }
}Because it may wrap non-Copy types. We would need to have specialization to get that fast path working. |
This comment has been minimized.
This comment has been minimized.
|
I imagine that if we introduce specialization, we will add a single impl: #[low_priority] // or whatever
impl<T: Copy> Clone for T {
fn clone(&self) -> Self { *self }
}and then just modify (Same for the other traits.) |
alexcrichton
reviewed
Apr 1, 2015
|
|
||
| # Unresolved questions | ||
|
|
||
| Is it reasonable to assume that no user who automatically derives `Eq`/`Ord` has manually implemented `PartialEq`/`PartialOrd`? If this assumption fails to hold, what behavior should result? |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Apr 1, 2015
Member
Cargo has a few examples of deriving Eq with a manual implementation of PartialEq. I'd be fine just moving some words from here to the "Drawbacks" section, though!
This comment has been minimized.
This comment has been minimized.
|
Why not "automatically derive supertraits of derived traits" in general? So |
This comment has been minimized.
This comment has been minimized.
|
@glaebhoerl hmm, good point. |
alexcrichton
reviewed
Apr 2, 2015
|
|
||
| An implementation of this RFC can be seen here: https://github.com/rust-lang/rust/pull/23905 | ||
|
|
||
| # Drawbacks |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Apr 2, 2015
Member
One other drawback this may wish to mention is that [T; N] is a type that can implement Copy for all N but not Clone. For example in landing rust-lang/rust#23860 it was discovered that many FFI types could derive Copy but not Clone because they had a field that was along the lines of [u8; 100].
It's quite easy, however, to add a Clone implementation for a type that is Copy, so the drawback would just be that today's #[derive(Copy)] + manual Clone would have to switch to a manual Copy as well (slightly more verbose).
This comment has been minimized.
This comment has been minimized.
nikomatsakis
Apr 3, 2015
Contributor
Note that @erickt's PR includes a specialized variant of Clone for the case where Copy is also mentioned and the types are not generic, which would have addressed this problem for FFI types at least. (Also this comment applies.)
This comment has been minimized.
This comment has been minimized.
nikomatsakis
self-assigned this
Apr 6, 2015
nikomatsakis
changed the title
Automatically derive certain traits when more fundamental traits are derived
Automatically derive {Clone,PartialEq,PartialOrd} when {Copy,Eq,Ord} are derived
Apr 6, 2015
This comment has been minimized.
This comment has been minimized.
|
I spiced up the title to make it clearer what is being requested. @bstrie, hope you don't mind. |
This comment has been minimized.
This comment has been minimized.
arthurprs
commented
Apr 8, 2015
|
@glaebhoerl seems like a great idea |
This comment has been minimized.
This comment has been minimized.
|
This should fix #441 if subtraits are all automatically derived. I think that's where @glaebhoerl originally suggested it. |
This comment has been minimized.
This comment has been minimized.
Kintaro
commented
Apr 8, 2015
|
I'm all for it. We still have time for breaking changes now before the final, so if we'd do it, now's the time. |
This comment has been minimized.
This comment has been minimized.
arthurprs
commented
Apr 8, 2015
|
@Kintaro This isn't a breaking change. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@arthurprs It is, see this post by @nikomatsakis on Discourse. (Edit: Jynx.) I think the customary approach in cases like this has been to gather some statistics on how much code would actually break? I suspect that it's not very much at all, and that this is worth doing. |
This comment has been minimized.
This comment has been minimized.
diwic
commented
Apr 8, 2015
|
Is it not possible to be intelligent enough to do: "Okay, we found a With that logic, would that make this RFC a non-breaking change? |
This comment has been minimized.
This comment has been minimized.
|
We can't avoid generating the impl entirely, but we could tag it with an attribute that would tell later stages to strip it if it would conflict with another impl. |
This comment has been minimized.
This comment has been minimized.
|
@diwic It might be possible, and if it is, it should definitely be done. However, it may be that expansion of item decorators happens before the session has collected all trait implementations, in which case it couldn't be done. |
This comment has been minimized.
This comment has been minimized.
|
I prefer it being explicit. Sure, in this case implicit might be ergonomically better here, but it doesn’t match the style of the language, which is explicit. |
This comment has been minimized.
This comment has been minimized.
|
@sfackler yes, that's specialization. If we were to add specialization in the future, we could add this backwards compatibilty. |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis I think he means adding a |
This comment has been minimized.
This comment has been minimized.
|
It wouldn't necessarily be specialization in this case, though, since the manual impl and the derived impl could be defined on the exact same bounds: #[derive(Copy)]
struct Thing(i32);
impl Clone for Thing {
fn clone(&self) -> Thing {
*self
}
}So we'd need a separate concept of a "low priority" or "weak" implementation or something that'll be discarded if there's any conflict whatsoever. |
This comment has been minimized.
This comment has been minimized.
|
I'm torn on this one. Overall, I do think I'm in favor, especially if we can add specialization in the future. |
This comment has been minimized.
This comment has been minimized.
|
I think "check if there's already a manual |
This comment has been minimized.
This comment has been minimized.
nstoddard
commented
Apr 8, 2015
|
I totally support this. I've always found it annoying how I have to derive both |
This comment has been minimized.
This comment has been minimized.
bluss
commented
Apr 8, 2015
|
We don't need a breaking change, can't we expand deriving in a non-breaking way? That way we have time to do it during a normal release cycle too and not do it in a rush. For example, what about I think we need to calmly look at nice generically useful solutions just like we used to. Always deriving multiple traits is less flexible, and useful in less situations, even though it is easier when it is applicable. Even just giving the trait group a new derive name is a simpler and more broadly useful solution: |
This comment has been minimized.
This comment has been minimized.
|
The fact that it's a more limited, special-case of specialization doesn't really make me feel better about it, to be honest. I'm definitely opposed to adding some last-minute magic into the trait system without a very strong justification. I want people to be able to understand deriving as simple shorthands for things they could have typed themselves. I don't think the breakage will be that bad if we just do this. In any case, we're trying to gather some numbers based on @brson's tool and PR rust-lang/rust#23905. |
This comment has been minimized.
This comment has been minimized.
comex
commented
Apr 8, 2015
|
In the space of non-breaking solutions, along with the proposed aliases that would cover the supertrait use cases here, I propose one that covers at least Clone, {Partial,}Eq, {Partial,}Ord, Debug, and Hash - i.e. the set of derivable traits that would be useful to implement (especially for the sake of users of a library type, or at worst harmless) for most simple structs with a few integer or string fields. What I'd call 'data-like' (as opposed to 'object-like') structs. I'd personally find this more useful than being able to omit supertraits but still having to write out several different traits for everything. (edited for clarity since I'm tired) |
This comment has been minimized.
This comment has been minimized.
|
@bluss @comex yes, it's true that shorthand names are a reasonable compromise -- and maybe a better solution. The last time we went through the exercise, we failed to come to much consensus about what those shorthands ought to be, but perhaps we're in a better position now. Things are more stable, and there are a lot more crates we could grep through to try to figure out the most common combinations. |
This comment has been minimized.
This comment has been minimized.
|
Another option would be to add |
This comment has been minimized.
This comment has been minimized.
|
@vadimcn |
This comment has been minimized.
This comment has been minimized.
bvssvni
commented
Apr 8, 2015
|
If specialization is planned at some point, I don't mind waiting for it, since that would be a backward compatible change. |
This comment has been minimized.
This comment has been minimized.
jpernst
commented
Apr 8, 2015
|
Speaking as a user who is generally in favor of inference, I have to say I'm against this. At least so long as manually implementing any trait that is also implicitly derived results in an error. Manually implementing one trait should not bar you from deriving another. If this problem can be solved more generally in the future, then this would be a great addition at that time. |
This comment has been minimized.
This comment has been minimized.
tedsta
commented
Apr 8, 2015
|
I am for this as long as the specialization gets implemented eventually. If there will never be a way to manually implement |
This comment has been minimized.
This comment has been minimized.
|
I think having thought this through I'm now feeling that it's not worth making a breaking change for this. Depending how things work out, we can either have some kind of shorthand notation or perhaps we'll be able to rely on specialization (we'd have to see). |
This comment has been minimized.
This comment has been minimized.
SiegeLord
commented
Apr 9, 2015
|
I'd rather have a shorthand than this RFC. My code would be broken by it, and I don't see the benefit given that there is a lint (at least for Copy + Clone). |
This comment has been minimized.
This comment has been minimized.
norcalli
commented
Apr 9, 2015
|
Logically, if I see something that says |
This comment has been minimized.
This comment has been minimized.
|
What's the point of a breaking change to save a few characters? Why not implement it in a non-breaking way (if you have a |
This comment has been minimized.
This comment has been minimized.
|
I'm in favour of this. It's weird that we allow the user to even ask for non-sensical derivations (e.g. "only Ord"). I'm sympathetic to manual implementations becoming a bit more verbose, but those are the exception, and we should optimize for the common case. This change makes Rust "just work" better. Also it allows us to make stronger assumptions about derived implementations: if they The fact that it saves key-strokes is simply gravy, but not the main point IMHO. |
This comment has been minimized.
This comment has been minimized.
|
I think I prefer the shorthand idea. @aturon suggested something like |
This comment has been minimized.
This comment has been minimized.
trws
commented
Apr 9, 2015
|
One option I haven't seen mentioned, forgive me if I missed it, would be to allow a user to specify which supertraits they wish to implement manually as part of the syntax. Something along the lines of |
This comment has been minimized.
This comment has been minimized.
|
After much deliberation, we've decided not to pursue this avenue, because it doesn't seem worth making a breaking change -- even a small one. Moreover, this change is not universally popular (e.g., 1, 2, 3) and may be pedagogically challenging, as it breaks the correspondence where deriving a trait is exactly equivalent to implement it (since implementing a trait does not automatically implement its supertraits). The alternative of creating shorthands seems to be a promising alternative, and might also go further in reducing verbosity by permitting groups of common traits to be group together. Therefore, I'm going to close this RFC as postponed under issue #441. @bstrie, thanks for writing this up and @erickt, thanks for the preliminary implementation. Thanks everybody else for the comments and thoughts. |
bstrie commentedApr 1, 2015
Rendered