|
| 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