-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Copy clone semantics #1521
Copy clone semantics #1521
Changes from all commits
d69cd0a
5542c80
91629f9
0feac23
df87d91
b9b7854
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
- Feature Name: N/A | ||
- Start Date: 01 March, 2016 | ||
- RFC PR: (leave this empty) | ||
- Rust Issue: (leave this empty) | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
With specialization on the way, we need to talk about the semantics of | ||
`<T as Clone>::clone() where T: Copy`. | ||
|
||
It's generally been an unspoken rule of Rust that a `clone` of a `Copy` type is | ||
equivalent to a `memcpy` of that type; however, that fact is not documented | ||
anywhere. This fact should be in the documentation for the `Clone` trait, just | ||
like the fact that `T: Eq` should implement `a == b == c == a` rules. | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
Currently, `Vec::clone()` is implemented by creating a new `Vec`, and then | ||
cloning all of the elements from one into the other. This is slow in debug mode, | ||
and may not always be optimized (although it often will be). Specialization | ||
would allow us to simply `memcpy` the values from the old `Vec` to the new | ||
`Vec` in the case of `T: Copy`. However, if we don't specify this, we will not | ||
be able to, and we will be stuck looping over every value. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will be stuck relying on the optimizer to figure it out. Which it can do and insert There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bluss exactly. |
||
|
||
It's always been the intention that `Clone::clone == ptr::read for T: Copy`; see | ||
[issue #23790][issue-copy]: "It really makes sense for `Clone` to be a | ||
supertrait of `Copy` -- `Copy` is a refinement of `Clone` where `memcpy` | ||
suffices, basically." This idea was also implicit in accepting | ||
[rfc #0839][rfc-extend] where "[B]ecause Copy: Clone, it would be backwards | ||
compatible to upgrade to Clone in the future if demand is high enough." | ||
|
||
# Detailed design | ||
[design]: #detailed-design | ||
|
||
Specify that `<T as Clone>::clone(t)` shall be equivalent to `ptr::read(t)` | ||
where `T: Copy, t: &T`. An implementation that does not uphold this *shall not* | ||
result in undefined behavior; `Clone` is not an `unsafe trait`. | ||
|
||
Also add something like the following sentence to the documentation for the | ||
`Clone` trait: | ||
|
||
"If `T: Copy`, `x: T`, and `y: &T`, then `let x = y.clone();` is equivalent to | ||
`let x = *y;`. Manual implementations must be careful to uphold this." | ||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
This is a breaking change, technically, although it breaks code that was | ||
malformed in the first place. | ||
|
||
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
The alternative is that, for each type and function we would like to specialize | ||
in this way, we document this separately. This is how we started off with | ||
`clone_from_slice`. | ||
|
||
# Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
What the exact wording should be. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternative wording: "When There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @petrochenkov it's not that it's unspecified whether There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why not? If we go this route anyway, let's give the compiler/libraries all the freedom.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, " There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm... At first I thought this was a bad idea, but then I started thinking. Not "always ignored", but if your There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really don't like the "optimizer magic overriding of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @arielb1 Yeah, I guess. I do think it would be better if it's just kept to libraries and language constructs like |
||
|
||
[issue-copy]: https://github.com/rust-lang/rust/issues/23790 | ||
[rfc-extend]: https://github.com/rust-lang/rfcs/blob/master/text/0839-embrace-extend-extinguish.md |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to cite rust-lang/rust#23790, which initially proposed making
Clone
a supertrait ofCopy
, with the description:The assumption that
Copy
andClone
would be compatible was also implicitly used in accepting RFC 839, as a premise thereof was the iea that it would be compatible to relax aT: Copy
bound to aT: Clone
bound later on.In other words, it's been more than an unspoken rule that clone and copy must be compatible, even if the rule seems not to have made its way onto the documentation of the
Clone
trait itself.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(The example of specializing
#[derive(Copy, Clone)]
that was brought up elsewhere in the thread also seems relevant.)