Skip to content

Commit da274a2

Browse files
committed
Split const generics in two and document inferred consts
1 parent 118fd1f commit da274a2

File tree

4 files changed

+188
-109
lines changed

4 files changed

+188
-109
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
- [Impl trait type](types/impl-trait.md)
9292
- [Type parameters](types/parameters.md)
9393
- [Inferred type](types/inferred.md)
94+
- [Const Generics](const-generics.md)
9495
- [Dynamically Sized Types](dynamically-sized-types.md)
9596
- [Type layout](type-layout.md)
9697
- [Interior mutability](interior-mutability.md)

src/const-generics.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
r[const-generics]
2+
# Const Generics
3+
4+
r[const-generics.argument]
5+
A const argument in a [path] specifies the const value to use for that item.
6+
7+
r[const-generics.argument.type]
8+
The argument must be a [const expression] of the type ascribed to the const
9+
parameter.
10+
11+
r[items.generics.const.type-ambiguity]
12+
When there is ambiguity if a generic argument could be resolved as either a
13+
type or const argument, it is always resolved as a type. Placing the argument
14+
in a block expression can force it to be interpreted as a const argument.
15+
16+
<!-- TODO: Rewrite the paragraph above to be in terms of namespaces, once
17+
namespaces are introduced, and it is clear which namespace each parameter
18+
lives in. -->
19+
20+
```rust,compile_fail
21+
type N = u32;
22+
struct Foo<const N: usize>;
23+
// The following is an error, because `N` is interpreted as the type alias `N`.
24+
fn foo<const N: usize>() -> Foo<N> { todo!() } // ERROR
25+
// Can be fixed by wrapping in braces to force it to be interpreted as the `N`
26+
// const parameter:
27+
fn bar<const N: usize>() -> Foo<{ N }> { todo!() } // ok
28+
```
29+
30+
r[items.generics.const.exhaustiveness]
31+
When resolving a trait bound obligation, the exhaustiveness of all
32+
implementations of const parameters is not considered when determining if the
33+
bound is satisfied. For example, in the following, even though all possible
34+
const values for the `bool` type are implemented, it is still an error that
35+
the trait bound is not satisfied:
36+
37+
```rust,compile_fail
38+
struct Foo<const B: bool>;
39+
trait Bar {}
40+
impl Bar for Foo<true> {}
41+
impl Bar for Foo<false> {}
42+
43+
fn needs_bar(_: impl Bar) {}
44+
fn generic<const B: bool>() {
45+
let v = Foo::<B>;
46+
needs_bar(v); // ERROR: trait bound `Foo<B>: Bar` is not satisfied
47+
}
48+
```
49+
50+
r[const-generics.kinds]
51+
There are three kinds of arguments to a const parameter:
52+
1. Standalone const parameters
53+
2. Inferred consts
54+
3. Arbitrary concrete expressions
55+
56+
r[const-generics.standalone]
57+
## Standalone const parameters
58+
59+
A const parameter can only be used in a const argument if it is a standalone usage.
60+
The argument must be *only* a usage of a const parameter and can be wrapped
61+
in at most one level of braces. That is, they cannot be combined with other
62+
expressions.
63+
```rust
64+
// Examples of standalone uses of const parameters
65+
66+
fn foo<const N: usize>() {
67+
let a: [u8; N] = [10; N];
68+
let b: [u8; { N }] = a;
69+
foo::<N>();
70+
foo::<{ N }>();
71+
}
72+
```
73+
74+
Here `a` has type `[u8; N]`, an array with a length of `N`, referring to `foo`'s const parameter.
75+
76+
```rust,compile_fail
77+
// Examples of non-standalone uses of const parameters
78+
79+
fn foo<const N: usize>() {
80+
let a: [u8; {{ N }}] = [10; (N)];
81+
foo::<{{ N }}>();
82+
foo::<(N)>();
83+
}
84+
```
85+
86+
r[const-generics.inferred]
87+
## Inferred consts
88+
89+
r[const-generics.inferred.syntax]
90+
```grammar,types
91+
@root InferredConst -> `_`
92+
```
93+
94+
The inferred const asks the compiler to infer the const if possible based on
95+
the surrounding information available.
96+
97+
It cannot be used in item signatures.
98+
99+
It is often used in repeat expressions:
100+
```rust
101+
fn make_array() -> [u32; 2] {
102+
[Default::default(); _]
103+
}
104+
```
105+
106+
r[const-generics.concrete-expr]
107+
## Concrete expressions
108+
109+
Most const expressions are allowed as const arguments:
110+
```rust
111+
// Example of a concrete expressions as an argument
112+
113+
fn make_array() -> [u8; 1 + 10 / 2] {
114+
[1; 6]
115+
}
116+
```
117+
118+
r[const-generics.concrete-expr.limitations]
119+
There are a few limitations about what expressions are allowed:
120+
1. Generic parameters may not be used
121+
2. In-scope where clauses may not be used
122+
3. Must be wrapped in braces in some cases
123+
124+
```rust,compile_fail
125+
// Examples where expressions may not be used as they use generic parameters.
126+
127+
// Not allowed in the const argument for an arrays length
128+
fn bad_function<const N: usize>() -> [u8; N + 1] {
129+
// Similarly not allowed for array repeat expressions' count argument.
130+
[1; N + 1]
131+
}
132+
133+
// Using type parameters is also disallowed
134+
fn type_parameters_disallowed<T>(_: [u8; size_of::<T>()]) {}
135+
```
136+
137+
```rust,compile_fail
138+
// Example where an expression may not be used as it depends on an in-scope
139+
// where clause
140+
141+
fn bad_function(_: [u8; { let a: [u8]; 1 }])
142+
where
143+
for<'a> [u8]: Sized, {}
144+
```
145+
146+
The const expression must be a [block expression][block](surrounded with braces) unless it's:
147+
- a single path segment (an [IDENTIFIER])
148+
- a [literal] (with a possibly leading `-` token)
149+
- an array length or repeat expression count
150+
151+
> [!NOTE]
152+
> This syntactic restriction is necessary to avoid requiring infinite lookahead when parsing an expression inside of a type.
153+
154+
```rust
155+
fn double<const N: i32>() {
156+
println!("doubled: {}", N * 2);
157+
}
158+
159+
const SOME_CONST: i32 = 12;
160+
161+
fn example() {
162+
// Example usage of a const argument.
163+
double::<9>();
164+
double::<-123>();
165+
double::<{7 + 8}>();
166+
double::<SOME_CONST>();
167+
double::<{ SOME_CONST + 5 }>();
168+
}
169+
```
170+
171+
[block]: ../expressions/block-expr.md
172+
[const expression]: ../const_eval.md#constant-expressions
173+
[literal]: ../expressions/literal-expr.md
174+
[path]: ../paths.md

src/items/generics.md

Lines changed: 11 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -67,36 +67,23 @@ The only allowed types of const parameters are `u8`, `u16`, `u32`, `u64`, `u128`
6767
`i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `char` and `bool`.
6868

6969
r[items.generics.const.usage]
70-
Const parameters can be used anywhere a [const item] can be used, with the
71-
exception that when used in a [type] or [array repeat expression], it must be
72-
standalone (as described below). That is, they are allowed in the following
73-
places:
74-
75-
1. As an applied const to any type which forms a part of the signature of the
76-
item in question.
77-
2. As part of a const expression used to define an [associated const], or as a
78-
parameter to an [associated type].
79-
3. As a value in any runtime expression in the body of any functions in the
80-
item.
81-
4. As a parameter to any type used in the body of any functions in the item.
82-
5. As a part of the type of any fields in the item.
83-
70+
Within an item that has const parameter declarations, the names of the const parameters are values:
8471
```rust
8572
// Examples where const generic parameters can be used.
8673

8774
// Used in the signature of the item itself.
8875
fn foo<const N: usize>(arr: [i32; N]) {
89-
// Used as a type within a function body.
76+
// Used within a type within a function body.
9077
let x: [i32; N];
91-
// Used as an expression.
78+
// Used within an expression.
9279
println!("{}", N * 2);
9380
}
9481

95-
// Used as a field of a struct.
82+
// Used within a field of a struct.
9683
struct Foo<const N: usize>([i32; N]);
9784

9885
impl<const N: usize> Foo<N> {
99-
// Used as an associated constant.
86+
// Used within an associated constant.
10087
const CONST: usize = N * 4;
10188
}
10289

@@ -105,13 +92,14 @@ trait Trait {
10592
}
10693

10794
impl<const N: usize> Trait for Foo<N> {
108-
// Used as an associated type.
95+
// Used within an associated type.
10996
type Output = [i32; N];
11097
}
11198
```
11299

113100
```rust,compile_fail
114-
// Examples where const generic parameters cannot be used.
101+
// Examples where const generic parameters cannot be used as the uses are not
102+
// within the item defining the const parameter.
115103
fn foo<const N: usize>() {
116104
// Cannot use in item definitions within a function body.
117105
const BAD_CONST: [usize; N] = [1; N];
@@ -124,71 +112,7 @@ fn foo<const N: usize>() {
124112
}
125113
```
126114

127-
r[items.generics.const.standalone]
128-
As a further restriction, const parameters may only appear as a standalone
129-
argument inside of a [type] or [array repeat expression]. In those contexts,
130-
they may only be used as a single segment [path expression], possibly inside a
131-
[block] (such as `N` or `{N}`). That is, they cannot be combined with other
132-
expressions.
133-
134-
```rust,compile_fail
135-
// Examples where const parameters may not be used.
136-
137-
// Not allowed to combine in other expressions in types, such as the
138-
// arithmetic expression in the return type here.
139-
fn bad_function<const N: usize>() -> [u8; {N + 1}] {
140-
// Similarly not allowed for array repeat expressions.
141-
[1; {N + 1}]
142-
}
143-
```
144-
145-
r[items.generics.const.argument]
146-
A const argument in a [path] specifies the const value to use for that item.
147-
148-
r[items.generics.const.argument.const-expr]
149-
The argument must be a [const expression] of the type ascribed to the const
150-
parameter. The const expression must be a [block expression][block]
151-
(surrounded with braces) unless it is a single path segment (an [IDENTIFIER])
152-
or a [literal] (with a possibly leading `-` token).
153-
154-
> [!NOTE]
155-
> This syntactic restriction is necessary to avoid requiring infinite lookahead when parsing an expression inside of a type.
156-
157-
```rust
158-
fn double<const N: i32>() {
159-
println!("doubled: {}", N * 2);
160-
}
161-
162-
const SOME_CONST: i32 = 12;
163-
164-
fn example() {
165-
// Example usage of a const argument.
166-
double::<9>();
167-
double::<-123>();
168-
double::<{7 + 8}>();
169-
double::<SOME_CONST>();
170-
double::<{ SOME_CONST + 5 }>();
171-
}
172-
```
173-
174-
r[items.generics.const.type-ambiguity]
175-
When there is ambiguity if a generic argument could be resolved as either a
176-
type or const argument, it is always resolved as a type. Placing the argument
177-
in a block expression can force it to be interpreted as a const argument.
178-
179-
<!-- TODO: Rewrite the paragraph above to be in terms of namespaces, once
180-
namespaces are introduced, and it is clear which namespace each parameter
181-
lives in. -->
182-
183-
```rust,compile_fail
184-
type N = u32;
185-
struct Foo<const N: usize>;
186-
// The following is an error, because `N` is interpreted as the type alias `N`.
187-
fn foo<const N: usize>() -> Foo<N> { todo!() } // ERROR
188-
// Can be fixed by wrapping in braces to force it to be interpreted as the `N`
189-
// const parameter:
190-
fn bar<const N: usize>() -> Foo<{ N }> { todo!() } // ok
191-
```
115+
There are limitations around how a const parameter can be used within a const argument ([const generics]).
192116

193117
r[items.generics.const.variance]
194118
Unlike type and lifetime parameters, const parameters can be declared without
@@ -207,26 +131,6 @@ struct Unconstrained;
207131
impl<const N: usize> Unconstrained {}
208132
```
209133

210-
r[items.generics.const.exhaustiveness]
211-
When resolving a trait bound obligation, the exhaustiveness of all
212-
implementations of const parameters is not considered when determining if the
213-
bound is satisfied. For example, in the following, even though all possible
214-
const values for the `bool` type are implemented, it is still an error that
215-
the trait bound is not satisfied:
216-
217-
```rust,compile_fail
218-
struct Foo<const B: bool>;
219-
trait Bar {}
220-
impl Bar for Foo<true> {}
221-
impl Bar for Foo<false> {}
222-
223-
fn needs_bar(_: impl Bar) {}
224-
fn generic<const B: bool>() {
225-
let v = Foo::<B>;
226-
needs_bar(v); // ERROR: trait bound `Foo<B>: Bar` is not satisfied
227-
}
228-
```
229-
230134
r[items.generics.where]
231135
## Where clauses
232136

@@ -290,10 +194,10 @@ struct Foo<#[my_flexible_clone(unbounded)] H> {
290194
[associated const]: associated-items.md#associated-constants
291195
[associated type]: associated-items.md#associated-types
292196
[attributes]: ../attributes.md
293-
[block]: ../expressions/block-expr.md
294197
[const contexts]: ../const_eval.md#const-context
295198
[const expression]: ../const_eval.md#constant-expressions
296199
[const item]: constant-items.md
200+
[const generics]: ../const-generics.md
297201
[enumerations]: enumerations.md
298202
[functions]: functions.md
299203
[function pointers]: ../types/function-pointer.md
@@ -303,7 +207,6 @@ struct Foo<#[my_flexible_clone(unbounded)] H> {
303207
[implementations]: implementations.md
304208
[item declarations]: ../statements.md#item-declarations
305209
[item]: ../items.md
306-
[literal]: ../expressions/literal-expr.md
307210
[path]: ../paths.md
308211
[path expression]: ../expressions/path-expr.md
309212
[raw pointers]: ../types/pointer.md#raw-pointers-const-and-mut
@@ -315,4 +218,4 @@ struct Foo<#[my_flexible_clone(unbounded)] H> {
315218
[type aliases]: type-aliases.md
316219
[type]: ../types.md
317220
[unions]: unions.md
318-
[value namespace]: ../names/namespaces.md
221+
[value namespace]: ../names/namespaces.md

src/patterns.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,8 @@ r[patterns.const.exhaustive]
973973

974974
r[patterns.const.generic]
975975
In particular, the value of `C` must be known at pattern-building time (which is pre-monomorphization).
976-
This means that associated consts that involve generic parameters cannot be used as patterns.
976+
This means that associated consts that involve generic parameters cannot be used as patterns and neither
977+
can uses of const parameters.
977978

978979
r[patterns.const.translation]
979980
After ensuring all conditions are met, the constant value is translated into a pattern, and now behaves exactly as-if that pattern had been written directly.

0 commit comments

Comments
 (0)