# const-generics-defaults stabilisation report
[stabilisation PR](https://github.com/rust-lang/rust/pull/90207)
# Stabilization report
This is the stabilization report for const parameter defaults and the removal of the type and const parameter ordering restriction. Its feature is `#![feature(const_generics_defaults)]`.
This report is a collaborative effort of @BoxyUwU and @lcnr.
## Summary
While supplying default arguments for type parameters is already possible on stable, this is not allowed for const parameters. Due to the restriction that type parameters must be in front of all const parameters, type parameter defaults cannot be used on stable if a const parameter exists.
This stabilization allows type and const parameters to be in an arbitrary order and adds the ability to specify default values for const parameters in all places where this is already allowed for type parameters. These defaults have the same restriction as other const arguments, so they must either be a fully concrete expression or another const parameter.
## Motivation
Const parameter defaults
- Reduces the difference between const generics and type generics by allowing both to have defaults
- Allows libraries to backwards compatibly add const generics to traits and types
- Makes working with const generics more ergonomic when there is a reasonable default for the generic parameter
Removal of the ordering restriction
- Type and const parameters are similar enough and there are cases where a specific order of generic parameters is prefered for clarity.
- With this restriction, it is not possible to have generics with both a type default (`T = 32`) and a const generic (`const N: u8`) without removing the ordering restriction
## Examples
```rust
struct ArrayStorage<T, const N: usize = 2> {
arr: [T; N],
}
impl<T> ArrayStorage<T> {
fn new(a: T, b: T) -> ArrayStorage<T> {
ArrayStorage {
arr: [a, b],
}
}
}
struct Image<
const WIDTH: usize,
const HEIGHT: usize,
// Without losing the ordering restriction,
// we could not add a default for this type parameter.
FORMAT: ImageFormat = PngFormat,
> {
// ...
}
// This order is a lot clearer than
//
// T, U, V, F, const N: usize, const M: usize
fn cartesian_product<
T, const N: usize,
U, const M: usize,
V, F
>(a: [T; N], b: [U; M]) -> [[V; N]; M]
where
F: FnMut(&T, &U) -> V
{
// ...
}
```
## Detailed feature description
### Ordering
The ordering restriction between types and consts is removed. The ordering of generic params is now: lifetimes, then type and const parameters.
We previously restricted the ordering due to implementation concerns, which have since been fixed.
#### Example
```rust
struct Foo<
T,
const N: usize,
U,
const M: usize = 3
V = [i64; M],
>(T, U, V);
fn foo<const N: usize, T, const M: usize>() { }
```
### Default const parameters
It is currently possible to provide a default type to type parameters in type and trait definition. With this stabilization it becomes possible to provide default values for const parameters as well.
The used syntax is `const PARAM_NAME: Ty = expr` where `expr` is either a single segment path, a literal, or surrounded by braces, e.g. `{ foo() }`. This restriction is identical to the restriction of const parameters and simplifies parsing.
Const defaults, for now, are not permitted to involve computations depending on generic parameters. This means that defaults may only be:
1. const expressions that do not depend on any generic parameters, e.g. `{ foo() + 1 }`, where `foo` is a const fn.
2. standalone const parameters, e.g. `N`. Note that both type and const parameter defaults may only refer to preceeding parameters, meaning that `struct Foo<const N: usize = M, const M: usize = 3>` causes an error.
When using an expression which does not evaluate successfully, e.g. `usize::MAX + 1`, as a const parameter default, we eagerly emit an error. This also mirrors the behavior of type parameter defaults.
#### Example
```rust
struct Foo<const N: usize = 10>;
struct Bar<const N: usize = { usize::MAX - 1 }, const M: usize = N>;
fn foo() -> (Foo, Bar<10>) {
(Foo::<10>, Bar)
}
struct Baz<const N: usize, const M: usize = { N + 1 }>;
//^ error: generic parameters may not be used in const operations
trait Trait<const N: usize> { const ASSOC: usize; }
pub struct UwU<const N: usize = { <()>::ASSOC }> where (): Trait<N>;
//^ error: no associated item named `ASSOC` found for unit type `()` in the current scope
```
## Documentation
The Book: Does not contain any documentation about const generics
The Reference: [reference#1098](https://github.com/rust-lang/reference/pull/1098)
## Important Tests
- Trait objects
- [trait_objects.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/trait_objects.rs)
- [trait_objects_fail.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/trait_objects_fail.rs)
- Impl Trait
- [rp_impl_trait.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/rp_impl_trait.rs)
- [rp_impl_trait_fail.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs)
- Wfness
- [wfness.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/wfness.rs)
- [default-param-wf-concrete.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs)
- Inference
- [doesnt_infer.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/doesnt_infer.rs) (tests that we dont use defaults to aide type inference)
- Ordering
- [forward-declared.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/forward-declared.rs)
- [intermixed-lifetime.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/intermixed-lifetime.rs)
- [wrong-order.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/wrong-order.rs)
- [complex-unord-param.rs](https://github.com/rust-lang/rust/tree/master/src/test/ui/const-generics/defaults/complex-unord-param.rs)
## Future Incompatibilty concerns
```rust
trait Trait<T> {
const ASSOC: usize;
}
impl Trait<()> for usize {
const ASSOC: usize = 2;
}
struct Foo<T, U, const N: usize = { usize::ASSOC }>(T, U)
where
usize: Trait<T> + Trait<U>;
```
`usize::ASSOC` is resolved today, but will be ambiguous if we start providing where clauses to type level constants. This is not a new issue however, as the following code has the same issue and currently compiles on stable:
```rust
fn foo<T, U>() -> [(); { usize::ASSOC }]
where
usize: Trait<T> + Trait<U> {
...
}
struct Foo<T, U>(T, U, [(); { usize::ASSOC }])
where
usize: Trait<T> + Trait<U>;
```
We therefore believe that this issue should not block the stabilization of const parameter defaults.