Skip to content

Commit 2a20c36

Browse files
authored
Merge pull request #2133 from arielb1/all-the-clones
RFC - compiler-generated Clone impls for Arrays and Tuples
2 parents f192404 + 4a4b3c5 commit 2a20c36

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

text/2133-all-the-clones.md

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
- Feature Name: all_the_clones
2+
- Start Date: 2017-08-28
3+
- RFC PR: https://github.com/rust-lang/rfcs/pull/2133
4+
- Rust Issue: https://github.com/rust-lang/rust/issues/44496
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Add compiler-generated `Clone` implementations for tuples and arrays with `Clone` elements of all lengths.
10+
11+
# Motivation
12+
[motivation]: #motivation
13+
14+
Currently, the `Clone` trait for arrays and tuples is implemented using a [macro] in libcore, for tuples of size 11 or less and for `Copy` arrays of size 32 or less. This breaks the uniformity of the language and annoys users.
15+
16+
Also, the compiler already implements `Copy` for all arrays and tuples with all elements `Copy`, which forces the compiler to provide an implementation for `Copy`'s supertrait `Clone`. There is no reason the compiler couldn't provide `Clone` impls for all arrays and tuples.
17+
18+
[macro]: https://github.com/rust-lang/rust/blob/f3d6973f41a7d1fb83029c9c0ceaf0f5d4fd7208/src/libcore/tuple.rs#L25
19+
20+
# Guide-level explanation
21+
[guide-level-explanation]: #guide-level-explanation
22+
23+
Arrays and tuples of `Clone` arrays are `Clone` themselves. Cloning them clones all of their elements.
24+
25+
# Reference-level explanation
26+
[reference-level-explanation]: #reference-level-explanation
27+
28+
Make `clone` a lang-item, add the following trait rules to the compiler:
29+
30+
```
31+
n number
32+
T type
33+
T: Clone
34+
----------
35+
[T; n]: Clone
36+
37+
T1,...,Tn types
38+
T1: Clone, ..., Tn: Clone
39+
----------
40+
(T1, ..., Tn): Clone
41+
```
42+
43+
And add the obvious implementations of `Clone::clone` and `Clone::clone_from` as MIR shim implementations, in the same manner as `drop_in_place`. The implementations could also do a shallow copy if the type ends up being `Copy`.
44+
45+
Remove the macro implementations in libcore. We still have macro implementations for other "derived" traits, such as `PartialEq`, `Hash`, etc.
46+
47+
Note that independently of this RFC, we're adding builtin `Clone` impls for all "scalar" types, most importantly fn pointer and fn item types (where manual impls are impossible in the foreseeable future because of higher-ranked types, e.g. `for<'a> fn(SomeLocalStruct<'a>)`), which are already `Copy`:
48+
```
49+
T fn pointer type
50+
----------
51+
T: Clone
52+
53+
T fn item type
54+
----------
55+
T: Clone
56+
57+
And just for completeness (these are perfectly done by an impl in Rust 1.19):
58+
59+
T int type | T uint type | T float type
60+
----------
61+
T: Clone
62+
63+
T type
64+
----------
65+
*const T: Clone
66+
*mut T: Clone
67+
68+
T type
69+
'a lifetime
70+
----------
71+
&'a T: Clone
72+
73+
----------
74+
bool: Clone
75+
char: Clone
76+
!: Clone
77+
```
78+
79+
This was considered a bug-fix (these types are all `Copy`, so it's easy to witness that they are `Clone`).
80+
81+
# Drawbacks
82+
[drawbacks]: #drawbacks
83+
84+
The MIR shims add complexity to the compiler. Along with the `derive(Clone)` implementation in `libsyntax`, we have 2 separate sets of implementations of `Clone`.
85+
86+
Having `Copy` and `Clone` impls for all arrays and tuples, but not `PartialEq` etc. impls, could be confusing to users.
87+
88+
# Rationale and Alternatives
89+
[alternatives]: #alternatives
90+
91+
Even with all proposed expansions to Rust's type-system, for consistency, the compiler needs to have at least *some* built-in `Clone` implementations: the type `for<'a> fn(Foo<'a>)` is `Copy` for all user-defined types `Foo`, but there is no way to implement `Clone`, which is a supertrait of `Copy`, for it (an `impl<T> Clone for fn(T)` won't match against the higher-ranked type).
92+
93+
The MIR shims for `Clone` of arrays and tuples are actually pretty simple and don't add much complexity after we have `drop_in_place` and shims for `Copy` types.
94+
95+
## The array situation
96+
97+
In Rust 1.19, arrays are `Clone` only if they are `Copy`. This code does not compile:
98+
```Rust
99+
fn main() {
100+
let x = [Box::new(0)].clone(); //~ ERROR
101+
println!("{:?}", x[0]);
102+
}
103+
```
104+
105+
~~The reason (I think) is that there is no good way to write a variable-length array expression in macros. This wouldn't be fixed by the first iteration of const generics.~~ Actually, this can be done using a for-loop (`ArrayVec` is used here instead of a manual panic guard for simplicity, but it can be easily implemented given const generics).
106+
```Rust
107+
impl<const n: usize; T: Clone> Clone for [T; n] {
108+
fn clone(&self) -> Self {
109+
unsafe {
110+
let result : ArrayVec<Self> = ArrayVec::new();
111+
for elem in (self as &[T]) {
112+
result.push(elem.clone());
113+
}
114+
result.into_inner().unwrap()
115+
}
116+
}
117+
}
118+
```
119+
120+
OTOH, this means that making non-`Copy` arrays `Clone` is less of a bugfix and more of a new feature. It's however a nice feature - `[Box<u32>; 1]` not being `Clone` is an annoying and seemingly-pointless edge case.
121+
122+
## Implement `Clone` only for `Copy` types
123+
124+
As of Rust 1.19, the compiler *does not* have the `Clone` implementations, which causes ICEs such as [rust-lang/rust#25733] because `Clone` is a supertrait of `Copy`.
125+
126+
One alternative, which would solve ICEs while being conservative, would be to have compiler implementations for `Clone` only for *`Copy`* tuples of size 12+ and arrays, and maintain the `libcore` macros for `Clone` of tuples (in Rust 1.19, arrays are only `Clone` if they are `Copy`).
127+
128+
This would make the shims *trivial* (a `Clone` implementation for a `Copy` type is just a memcpy), and would not implement any features that are not needed.
129+
130+
When we get variadic generics, we could make all tuples with `Clone` elements `Clone`. When we get const generics, we could make all arrays with `Clone` elements `Clone`.
131+
132+
## Use a MIR implementation of `Clone` for all derived impls
133+
134+
The implementation on the other end of the conservative-radical end would be to use the MIR shims for *all* `#[derive(Clone)]` implementations. This would increase uniformity by getting rid of the separate `libsyntax` derived implementation. However:
135+
136+
1. We'll still need the `#[derive_Clone]` hook in libsyntax, which would presumably result in an attribute that trait selection can see. That's not a significant concern.
137+
138+
2. The more annoying issue is that, as a workaround to trait matching being inductive, derived implementations are imperfect - see [rust-lang/rust#26925]. This means that we either have to solve that issue for `Clone` (which is dedicatedly non-trivial) or have some sort of type-checking for the generated MIR shims, both annoying options.
139+
140+
3. A MIR shim implementation would also have to deal with edge cases such as `#[repr(packed)]`, which normal type-checking would handle for ordinary `derive`. I think drop glue already encounters all of these edge cases so we have to deal with them anyway.
141+
142+
## `Copy` and `Clone` for closures
143+
144+
We could also add implementations of `Copy` and `Clone` to closures. That is [RFC #2132] and should be discussed there.
145+
146+
# Unresolved questions
147+
[unresolved]: #unresolved-questions
148+
149+
See Alternatives.
150+
151+
[RFC #2132]: https://github.com/rust-lang/rfcs/pull/2132
152+
[rust-lang/rust#25733]: https://github.com/rust-lang/rust/issues/25733
153+
[rust-lang/rust#26925]: https://github.com/rust-lang/rust/issues/26925

0 commit comments

Comments
 (0)