Skip to content

Commit ba2f040

Browse files
committed
Move generic constants to a dedicated chapter
1 parent c78a8a3 commit ba2f040

5 files changed

Lines changed: 239 additions & 235 deletions

File tree

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
- [Type parameters](types/parameters.md)
9494
- [Inferred type](types/inferred.md)
9595
- [Generics](types/generics/index.md)
96+
- [Generic constants](types/generics/constants.md)
9697
- [Dynamically sized types](dynamically-sized-types.md)
9798
- [Type layout](type-layout.md)
9899
- [Interior mutability](interior-mutability.md)

src/expressions/array-expr.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ The array index expression can be implemented for types other than arrays and sl
136136
[const block expression]: expr.block.const
137137
[constant expression]: ../const_eval.md#constant-expressions
138138
[constant item]: ../items/constant-items.md
139-
[inferred const]: generics.const.inferred
139+
[inferred const]: generics.const.arguments.inferred
140140
[literal]: ../tokens.md#literals
141141
[memory location]: ../expressions.md#place-expressions-and-value-expressions
142142
[panic]: ../panic.md

src/syntax-index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ This appendix provides an index of tokens and common forms with links to where t
340340
[if let]: expr.if.let
341341
[impl trait types]: type.impl-trait.return
342342
[implementations]: items.impl
343-
[inferred const]: generics.const.inferred
343+
[inferred const]: generics.const.arguments.inferred
344344
[inferred type]: type.inferred
345345
[infinite loop expressions]: expr.loop.infinite
346346
[infinite loops]: expr.loop.infinite

src/types/generics/constants.md

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
r[generics.const]
2+
# Generic constants
3+
4+
r[generics.const.intro]
5+
*Generic constant parameters* allow items to be generic over constant values.
6+
7+
> [!EXAMPLE]
8+
> ```rust
9+
> struct Grid<const WIDTH: usize, const HEIGHT: usize> {
10+
> data: [[f32; WIDTH]; HEIGHT],
11+
> }
12+
>
13+
> impl<const WIDTH: usize, const HEIGHT: usize> Grid<WIDTH, HEIGHT> {
14+
> fn new() -> Self {
15+
> Grid { data: [[0.0; WIDTH]; HEIGHT] }
16+
> }
17+
>
18+
> fn size(&self) -> usize {
19+
> WIDTH * HEIGHT
20+
> }
21+
> }
22+
>
23+
> let grid: Grid<4, 4> = Grid::new();
24+
> assert_eq!(grid.size(), 16);
25+
> ```
26+
27+
r[generics.const.namespace]
28+
The const identifier introduces a name in the [value namespace] for the constant parameter, and all instances of the item must be instantiated with a value of the given type.
29+
30+
r[generics.const.allowed-types]
31+
The only allowed types of const parameters are `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `char` and `bool`.
32+
33+
r[generics.const.use]
34+
Const parameters can be used anywhere a [const item] can be used, with the exception that when used in a [type] or [array repeat expression], it must be standalone (as described below). That is, they are allowed in the following places:
35+
36+
1. As an applied const to any type which forms a part of the signature of the item in question.
37+
2. As part of a const expression used to define an [associated const], or as a parameter to an [associated type].
38+
3. As a value in any runtime expression in the body of any functions in the item.
39+
4. As a parameter to any type used in the body of any functions in the item.
40+
5. As a part of the type of any fields in the item.
41+
42+
> [!EXAMPLE]
43+
> ```rust
44+
> // Examples where generic constant parameters can be used.
45+
>
46+
> // Used in the signature of the item itself.
47+
> fn foo<const N: usize>(arr: [i32; N]) {
48+
> // Used as a type within a function body.
49+
> let x: [i32; N];
50+
> // Used as an expression.
51+
> println!("{}", N * 2);
52+
> }
53+
>
54+
> // Used as a field of a struct.
55+
> struct Foo<const N: usize>([i32; N]);
56+
>
57+
> impl<const N: usize> Foo<N> {
58+
> // Used as an associated constant.
59+
> const CONST: usize = N * 4;
60+
> }
61+
>
62+
> trait Trait {
63+
> type Output;
64+
> }
65+
>
66+
> impl<const N: usize> Trait for Foo<N> {
67+
> // Used as an associated type.
68+
> type Output = [i32; N];
69+
> }
70+
> ```
71+
>
72+
> ```rust,compile_fail
73+
> // Examples where generic constant parameters cannot be used.
74+
> fn foo<const N: usize>() {
75+
> // Cannot use in item definitions within a function body.
76+
> const BAD_CONST: [usize; N] = [1; N];
77+
> static BAD_STATIC: [usize; N] = [1; N];
78+
> fn inner(bad_arg: [usize; N]) {
79+
> let bad_value = N * 2;
80+
> }
81+
> type BadAlias = [usize; N];
82+
> struct BadStruct([usize; N]);
83+
> }
84+
> ```
85+
86+
r[generics.const.standalone]
87+
As a further restriction, const parameters may only appear as a standalone argument inside of a [type] or [array repeat expression]. In those contexts, they may only be used as a single segment [path expression], possibly inside a [block] (such as `N` or `{N}`). That is, they cannot be combined with other expressions.
88+
89+
> [!EXAMPLE]
90+
> ```rust,compile_fail
91+
> // Examples where const parameters may not be used.
92+
>
93+
> // Not allowed to combine in other expressions in types, such as the
94+
> // arithmetic expression in the return type here.
95+
> fn bad_function<const N: usize>() -> [u8; {N + 1}] {
96+
> // Similarly not allowed for array repeat expressions.
97+
> [1; {N + 1}]
98+
> }
99+
> ```
100+
101+
r[generics.const.variance]
102+
Unlike type and lifetime parameters, const parameters can be declared without being used inside of a parameterized item, with the exception of implementations as described in [generic implementations]:
103+
104+
> [!EXAMPLE]
105+
> ```rust,compile_fail
106+
> // ok
107+
> struct Foo<const N: usize>;
108+
> enum Bar<const M: usize> { A, B }
109+
>
110+
> // ERROR: unused parameter
111+
> struct Baz<T>;
112+
> struct Biz<'a>;
113+
> struct Unconstrained;
114+
> impl<const N: usize> Unconstrained {}
115+
> ```
116+
117+
r[generics.const.exhaustiveness]
118+
When resolving a trait bound obligation, the exhaustiveness of all implementations of const parameters is not considered when determining if the bound is satisfied. For example, in the following, even though all possible const values for the `bool` type are implemented, it is still an error that the trait bound is not satisfied:
119+
120+
> [!EXAMPLE]
121+
> ```rust,compile_fail
122+
> struct Foo<const B: bool>;
123+
> trait Bar {}
124+
> impl Bar for Foo<true> {}
125+
> impl Bar for Foo<false> {}
126+
>
127+
> fn needs_bar(_: impl Bar) {}
128+
> fn generic<const B: bool>() {
129+
> let v = Foo::<B>;
130+
> needs_bar(v); // ERROR: trait bound `Foo<B>: Bar` is not satisfied
131+
> }
132+
> ```
133+
134+
r[generics.const.arguments]
135+
## Const arguments
136+
137+
r[generics.const.arguments.intro]
138+
A *const argument* specifies the const value to use for a const parameter.
139+
140+
r[generics.const.arguments.const-expr]
141+
A const argument must either be an [inferred const] or be a [const expression] of the type ascribed to the const parameter.
142+
143+
> [!NOTE]
144+
> In a generic argument list, an [inferred const] is parsed as an [inferred type][InferredType] but then semantically treated as a separate kind of [generic const argument].
145+
146+
r[generics.const.arguments.complex-const-params]
147+
Const argument expressions must be surrounded by braces unless they are a [literal] (with a possibly leading `-` token) or a single segment path.
148+
149+
> [!NOTE]
150+
> This syntactic restriction is necessary to avoid requiring infinite lookahead when parsing an expression inside of a type.
151+
152+
> [!EXAMPLE]
153+
> ```rust
154+
> struct S<const N: i64>;
155+
> const C: i64 = 1;
156+
> fn f<const N: i64>() -> S<N> { S }
157+
>
158+
> let _ = f::<1>(); // Literal.
159+
> let _ = f::<-1>(); // Negative literal.
160+
> let _ = f::<{ 1 + 2 }>(); // Constant expression.
161+
> let _ = f::<C>(); // Single segment path.
162+
> let _ = f::<{ C + 1 }>(); // Constant expression.
163+
> let _: S<1> = f::<_>(); // Inferred const.
164+
> let _: S<1> = f::<(((_)))>(); // Inferred const.
165+
> ```
166+
>
167+
> ```rust,compile_fail
168+
> fn f<const N: usize>() -> [u8; N] { [0; _] }
169+
> let _: [_; 1] = f::<{ _ }>();
170+
> // ^ ERROR `_` not allowed here
171+
> ```
172+
173+
r[generics.const.arguments.type-ambiguity]
174+
When there is ambiguity if a generic argument could be resolved as either a type or const argument, it is always resolved as a type. Placing the argument in a block expression can force it to be interpreted as a const argument.
175+
176+
<!-- TODO: Rewrite the paragraph above to be in terms of namespaces, once namespaces are introduced, and it is clear which namespace each parameter lives in. -->
177+
178+
> [!EXAMPLE]
179+
> ```rust,compile_fail
180+
> type N = u32;
181+
> struct Foo<const N: usize>;
182+
> // The following is an error, because `N` is interpreted as the type alias `N`.
183+
> fn foo<const N: usize>() -> Foo<N> { todo!() } // ERROR
184+
> // Can be fixed by wrapping in braces to force it to be interpreted as the `N`
185+
> // const parameter:
186+
> fn bar<const N: usize>() -> Foo<{ N }> { todo!() } // ok
187+
> ```
188+
189+
r[generics.const.arguments.inferred]
190+
## Inferred const arguments
191+
192+
r[generics.const.arguments.inferred.intro]
193+
An *inferred const* is a const argument specified with `_`. This asks the compiler to infer the const argument if possible based on surrounding information.
194+
195+
> [!EXAMPLE]
196+
> ```rust
197+
> fn make_buf<const N: usize>() -> [u8; N] {
198+
> [0; _]
199+
> // ^ Infers `N`.
200+
> }
201+
> let _: [u8; 1024] = make_buf::<_>();
202+
> // ^ Infers `1024`.
203+
> ```
204+
205+
> [!NOTE]
206+
> An [inferred const] is not semantically an [expression][Expression] and so is not accepted within braces.
207+
>
208+
> ```rust,compile_fail
209+
> fn f<const N: usize>() -> [u8; N] { [0; _] }
210+
> let _: [_; 1] = f::<{ _ }>();
211+
> // ^ ERROR `_` not allowed here
212+
> ```
213+
214+
r[generics.const.arguments.inferred.signature]
215+
The inferred const cannot be used in item signatures.
216+
217+
> [!EXAMPLE]
218+
> ```rust,compile_fail
219+
> fn f<const N: usize>(x: [u8; N]) -> [u8; _] { x }
220+
> // ^ ERROR not allowed
221+
> ```
222+
223+
[array repeat expression]: expr.array
224+
[associated const]: items.associated.const
225+
[associated type]: items.associated.type
226+
[block]: expr.block
227+
[const expression]: const-eval.const-expr
228+
[const item]: items.const
229+
[generic const argument]: generics.const.arguments
230+
[generic implementations]: items.impl.generics
231+
[inferred const]: generics.const.arguments.inferred
232+
[literal]: expr.literal
233+
[path expression]: expr.path
234+
[value namespace]: names.namespaces

0 commit comments

Comments
 (0)