Description
What is this
This is a design document for const generics. Any discussions about its content should be on zulip. The conclusions of these discussions should then be edited back into this issue. Please do not post any comments directly in this issue.
(FWIW: I don't like this policy, but will abide by it.)
Content
This was on my mind for a couple of reasons, namely the generic integers v2 RFC and the lack of Default
for arrays of arbitrary size, among other things.
Although it hasn't been entirely solidified with regard to const generics (see: #29, rust-lang/rust#95174 (comment)), it seems that the general consensus regarding structural equality has been settled: pattern-matching is the canonical way that equality works for constants.
A lot of existing proposals recommend expressions for constant bounds, which always felt far out of reach, and in particular requires generic_const_exprs
. But patterns seem pretty easy to reconcile:
impl<T> Default for [T; 0] {
fn default() -> [T; 0] { [] }
}
impl<T, const N: usize> Default for [T; N]
where
N in 1..,
{
fn default() -> [T; N] {
let mut x = [const { MaybeUninit::uninit() }; N];
let mut idx = 0;
while idx < N {
x[idx].write(T::default());
idx += 1;
}
x.map(MaybeUninit::assume_init)
}
}
Note that the CONST in PAT
syntax here is entirely replaceable with something else; I just chose something that would be usable for an initial implementation, and in
is one of the few keywords I figured would work. I don't like using anything that looks like an expression, since N = 1..
would visually look like setting the value equal to the RangeFrom
expression, not matching a pattern.
The idea here is that we already have the mechanisms to check for exhaustiveness with regards to constants and patterns, and so we could just reuse this machinery for const generics. Additionally, it means that any usages of these impls would have to add these patterns to the bounds unless they were completely exhaustive, i.e., something like this:
impl<const N: usize> MyTrait for [T; N] where N in 1..=2 {}
impl<const N: usize> MyTrait for [T; N] where N in 10..=20 {}
// ...
any equivalent bounds would be required to add the relevant where
bounds of the form N in 1..=2 | 10..=20 /* | ... */
, or stricter.
I hadn't seen this design proposed before, and decided it would be worth writing up. Technically, it could also be used to implement multiple const-generic impls at once, if you did something like:
impl<const N: usize> MyTrait for [T; N] where N in 8 | 16 | 32 | 64 | 128 {}
And I suppose that would be fine. We could probably find a way to detect anything of the form:
impl<const N: usize> MyTrait for [T; N] where N in CONST {}
And suggest replacing it with impl MyTrait for [T; CONST] {}
. (Barring all the rules for generic_const_exprs
also apply.)