😱 Status quo story: Barbara wants to implement Default for all array lengths

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)

She thinks "Ah, I can use min-const-generics for this!" and goes to write

#![allow(unused)] fn main() { impl<T, const N: usize> Default for [T; N] where T: Default, { fn default() -> Self { } } }

So far so good, but then she realizes she can't figure out what to write in the body. At first she tries:

#![allow(unused)] fn main() { impl<T, const N: usize> Default for [T; N] where T: Default, { fn default() -> Self { [T::default(); N] } } }

but this won't compile:

error[E0277]: the trait bound `T: Copy` is not satisfied --> src/lib.rs:10:9 | 10 | [T::default(); N] | ^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | = note: the `Copy` trait is required because the repeated element will be copied help: consider further restricting this bound | 7 | T: MyDefault + Copy, | ^^^^^^

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

She asks on Zulip and lcnr tells her that there is this map function defined on arrays. She could write:

#![allow(unused)] fn main() { impl<T, const N: usize> Default for [T; N] where T: Default, { fn default() -> Self { [(); N].map(|()| T::default()) } } }

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

#![allow(unused)] fn main() { fn generic<T>() -> [T; 0] { Default::default() } }

"Ah," she says, "I see that Default is implemented for any type [T; 0], regardless of whether T: Default. That makes sense. Argh!"

Next she tries (this already relies on a nightly feature)

#![allow(unused)] fn main() { impl<T: Trait, const N: usize> Default for [T; N] where T: Default, N != 0, // nightly feature! { fn default() -> Self { [(); N].map(|()| T::default()) } } impl<T> Trait for [T; 0] { // ... } }

While this does seem to compile, when trying to use it, it causes an unexpected error.

#![allow(unused)] fn main() { fn foo<T: Trait, const N: usize>() -> [u8; N] { <[T; N] as Trait>::ASSOC //~ ERROR `[T; N]` does not implement `Trait` } }

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.

Frustrated, Barbara gives up and goes looking for another issue to fix.

Even worse, Barbara notices the same problem for serde::Deserialize and decides to abandon backwards compatibility in favor of a brighter future.