|
| 1 | +# 😱 Status quo story: Barbara wants to implement `Default` for all array lengths |
| 2 | + |
| 3 | +Barbara is working on `std`. She wants to implement `Default` for all array types. Currently, it is implemented for `N <= 32` using a macro ([link](https://doc.rust-lang.org/nightly/src/core/array/mod.rs.html#391)) |
| 4 | + |
| 5 | +She thinks "Ah, I can use min-const-generics for this!" and goes to write |
| 6 | + |
| 7 | +```rust |
| 8 | +impl<T, const N: usize> Default for [T; N] |
| 9 | +where |
| 10 | + T: Default, |
| 11 | +{ |
| 12 | + fn default() -> Self { |
| 13 | + |
| 14 | + } |
| 15 | + |
| 16 | +} |
| 17 | +``` |
| 18 | + |
| 19 | +So far so good, but then she realizes she can't figure out what to write in the body. At first she tries: |
| 20 | + |
| 21 | +```rust |
| 22 | +impl<T, const N: usize> Default for [T; N] |
| 23 | +where |
| 24 | + T: Default, |
| 25 | +{ |
| 26 | + fn default() -> Self { |
| 27 | + [T::default(); N] |
| 28 | + } |
| 29 | + |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +but this won't compile: |
| 34 | + |
| 35 | +``` |
| 36 | +error[E0277]: the trait bound `T: Copy` is not satisfied |
| 37 | + --> src/lib.rs:10:9 |
| 38 | + | |
| 39 | +10 | [T::default(); N] |
| 40 | + | ^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` |
| 41 | + | |
| 42 | + = note: the `Copy` trait is required because the repeated element will be copied |
| 43 | +help: consider further restricting this bound |
| 44 | + | |
| 45 | +7 | T: MyDefault + Copy, |
| 46 | + | ^^^^^^ |
| 47 | +``` |
| 48 | + |
| 49 | +"Ah," she realizes, "this would be cloning a single value of `T`, but I want to make `N` distinct values. How can I do that?" |
| 50 | + |
| 51 | +She asks on Zulip and lcnr tells her that there is this [`map` function defined on arrays](https://doc.rust-lang.org/std/primitive.array.html#method.map). She could write: |
| 52 | + |
| 53 | + |
| 54 | +```rust |
| 55 | +impl<T, const N: usize> Default for [T; N] |
| 56 | +where |
| 57 | + T: Default, |
| 58 | +{ |
| 59 | + fn default() -> Self { |
| 60 | + [(); N].map(|()| T::default()) |
| 61 | + } |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +"That code will build," lcnr warns her, "but we're not going to be able to ship it. Test it and see." Barbara runs the tests and finds she is getting a failure. The following test no longer builds: |
| 66 | + |
| 67 | +```rust |
| 68 | +fn generic<T>() -> [T; 0] { |
| 69 | + Default::default() |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +"Ah," she says, "I see that `Default` is implemented for any type `[T; 0]`, regardless of whether `T: Default`. That makes sense. Argh!" |
| 74 | + |
| 75 | +Next she tries (this already relies on a nightly feature) |
| 76 | +```rust |
| 77 | +impl<T: Trait, const N: usize> Default for [T; N] |
| 78 | +where |
| 79 | + T: Default, |
| 80 | + N != 0, // nightly feature! |
| 81 | +{ |
| 82 | + fn default() -> Self { |
| 83 | + [(); N].map(|()| T::default()) |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +impl<T> Trait for [T; 0] { |
| 88 | + // ... |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +While this does seem to compile, when trying to use it, it causes an unexpected error. |
| 93 | + |
| 94 | +```rust |
| 95 | +fn foo<T: Trait, const N: usize>() -> [u8; N] { |
| 96 | + <[T; N] as Trait>::ASSOC //~ ERROR `[T; N]` does not implement `Trait` |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +The compiler can't tell that `N == 0 || N != 0` is true for all possible `N`, so it can't infer `[T; N]: Trait` from `T: Trait`. |
| 101 | + |
| 102 | +Frustrated, Barbara gives up and goes looking for another issue to fix. |
| 103 | + |
| 104 | +Even worse, Barbara notices the same problem for `serde::Deserialize` and decides to |
| 105 | +abandon backwards compatibility in favor of a brighter future. |
0 commit comments