Skip to content

Commit ebb5552

Browse files
committed
Add more details about const generics.
1 parent 6dc5c2f commit ebb5552

File tree

1 file changed

+145
-14
lines changed

1 file changed

+145
-14
lines changed

src/items/generics.md

+145-14
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
> _ConstParam_:\
1818
>    `const` [IDENTIFIER] `:` [_Type_]
1919
20-
Functions, type aliases, structs, enumerations, unions, traits, and
21-
implementations may be *parameterized* by types, constants, and lifetimes. These
20+
[Functions], [type aliases], [structs], [enumerations], [unions], [traits], and
21+
[implementations] may be *parameterized* by types, constants, and lifetimes. These
2222
parameters are listed in angle <span class="parenthetical">brackets (`<...>`)</span>,
2323
usually immediately after the name of the item and before its definition. For
2424
implementations, which don't have a name, they come directly after `impl`.
@@ -33,18 +33,137 @@ struct Ref<'a, T> where T: 'a { r: &'a T }
3333
struct InnerArray<T, const N: usize>([T; N]);
3434
```
3535

36+
The generic parameters are in scope within the item definition where they are
37+
declared.
38+
39+
[References], [raw pointers], [arrays], [slices][arrays], [tuples], and
40+
[function pointers] have lifetime or type parameters as well, but are not
41+
referred to with path syntax.
42+
43+
### Const generics
44+
45+
Const generic parameters allow items to be generic over constant values. The
46+
const identifier introduces a name for the constant parameter, and all
47+
instances of the item must be instantiated with a value of the given type.
48+
49+
<!-- TODO: update above to say "introduces a name in the [value namespace]"
50+
once namespaces are added. -->
51+
3652
The only allowed types of const parameters are `u8`, `u16`, `u32`, `u64`, `u128`, `usize`
3753
`i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `char` and `bool`.
3854

39-
Const parameters may only be be used as standalone arguments inside
40-
of [types] and [repeat expressions] but may be freely used elsewhere:
55+
Const parameters can generally be used anywhere a [const item] can be used,
56+
with the exception of the definition of any [item] within the body of a
57+
function, and can only be used as standalone expressions in [types] and
58+
[array repeat expressions] (described below). That is, they are allowed in the
59+
following places:
60+
61+
1. As an applied const to any type which forms a part of the signature of the
62+
item in question.
63+
2. As part of a const expression used to define an [associated const], or as a
64+
parameter to an [associated type].
65+
3. As a value in any runtime expression in the body of any functions in the
66+
item.
67+
4. As a parameter to any type used in the body of any functions in the item.
68+
5. As a part of the type of any fields in the item.
69+
70+
```rust
71+
// Examples where const generic parameters can be used.
72+
73+
// Used in the signature of the item itself.
74+
fn foo<const N: usize>(arr: [i32; N]) {
75+
// Used as a type within a function body.
76+
let x: [i32; N];
77+
// Used as an expression.
78+
println!("{}", N * 2);
79+
}
80+
81+
// Used as a field of a struct.
82+
struct Foo<const N: usize>([i32; N]);
83+
84+
impl<const N: usize> Foo<N> {
85+
// Used as an associated constant.
86+
const CONST: usize = N * 4;
87+
}
88+
89+
trait Trait {
90+
type Output;
91+
}
92+
93+
impl<const N: usize> Trait for Foo<N> {
94+
// Used as an associated type.
95+
type Output = [i32; N];
96+
}
97+
```
98+
99+
```rust,compile_fail
100+
// Examples where const generic parameters cannot be used.
101+
fn foo<const N: usize>() {
102+
// Cannot use in item definitions within a function body.
103+
const BAD_CONST: [usize; N] = [1; N];
104+
static BAD_STATIC: [usize; N] = [1; N];
105+
fn inner(bad_arg: [usize; N]) {
106+
let bad_value = N * 2;
107+
}
108+
type BadAlias = [usize; N];
109+
struct BadStruct([usize; N]);
110+
}
111+
```
112+
113+
As a further restriction, const parameters may only appear as a standalone
114+
argument inside of [types] and [array repeat expressions]. In those contexts,
115+
they may only be used as a single segment [path expression], possibly inside a
116+
[block] (such as `N` or `{N}`). That is, they cannot be combined with other
117+
expressions.
41118

42119
```rust,compile_fail
43-
// ok: standalone argument
44-
fn foo<const N: usize>() -> [u8; N] { todo!() }
120+
// Examples where const parameters may not be used.
121+
122+
// Not allowed to combine in other expressions in types, such as the
123+
// arithmetic expression in the return type here.
124+
fn bad_function<const N: usize>() -> [u8; {N + 1}] {
125+
// Similarly not allowed for array repeat expressions.
126+
[1; {N + 1}]
127+
}
128+
```
129+
130+
A const argument in a [path] specifies the const value to use for that item.
131+
The argument must be a [const expression] of the type ascribed to the const
132+
parameter. The const expression must be a [block expression][block]
133+
(surrounded with braces) unless it is a single path segment (an [IDENTIFIER])
134+
or a [literal] (with a possibly leading `-` token). This syntactic restriction
135+
is necessary to avoid requiring infinite lookahead when parsing an expression
136+
inside of a type.
137+
138+
```rust
139+
fn double<const N: i32>() {
140+
println!("doubled: {}", N * 2);
141+
}
45142

46-
// ERROR: generic const operation
47-
fn bar<const N: usize>() -> [u8; N + 1] { todo!() }
143+
const SOME_CONST: i32 = 12;
144+
145+
fn example() {
146+
// Example usage of a const argument.
147+
double::<9>();
148+
double::<-123>();
149+
double::<{7 + 8}>();
150+
double::<SOME_CONST>();
151+
double::<{ SOME_CONST + 5 }>();
152+
}
153+
```
154+
155+
When there is ambiguity if a generic argument could be resolved as either a
156+
type or const argument, it is always resolved as a type. Placing the argument
157+
in a block expression can force it to be interpreted as a const argument.
158+
159+
```rust,compile_fail
160+
type N = u32;
161+
struct Foo<const N: usize>;
162+
// The following is an error, because `N` is interpreted as the type alias `N`.
163+
fn foo<const N: usize>() -> Foo<N> { todo!() } // ERROR
164+
// Can be fixed by wrapping in braces to force it to be interprted as the `N`
165+
// const parameter:
166+
fn bar<const N: usize>() -> Foo<{ N }> { todo!() } // ok
48167
```
49168

50169
Unlike type and lifetime parameters, const parameters of types can be used without
@@ -60,10 +179,6 @@ struct Baz<T>;
60179
struct Biz<'a>;
61180
```
62181

63-
[References], [raw pointers], [arrays], [slices][arrays], [tuples], and
64-
[function pointers] have lifetime or type parameters as well, but are not
65-
referred to with path syntax.
66-
67182
## Where clauses
68183

69184
> **<sup>Syntax</sup>**\
@@ -90,7 +205,7 @@ parameters.
90205
The `for` keyword can be used to introduce [higher-ranked lifetimes]. It only
91206
allows [_LifetimeParam_] parameters.
92207

93-
Bounds that don't use the item's parameters or higher-ranked lifetimes are
208+
Bounds that don't use the item's parameters or [higher-ranked lifetimes] are
94209
checked when the item is defined. It is an error for such a bound to be false.
95210

96211
[`Copy`], [`Clone`], and [`Sized`] bounds are also checked for certain generic
@@ -141,17 +256,33 @@ struct Foo<#[my_flexible_clone(unbounded)] H> {
141256
[_Type_]: ../types.md#type-expressions
142257
[_TypeParamBounds_]: ../trait-bounds.md
143258

259+
[array repeat expressions]: ../expressions/array-expr.md
144260
[arrays]: ../types/array.md
261+
[associated const]: associated-items.md#associated-constants
262+
[associated type]: associated-items.md#associated-types
263+
[block]: ../expressions/block-expr.md
145264
[const contexts]: ../const_eval.md#const-context
265+
[const expression]: ../const_eval.md#constant-expressions
266+
[const item]: constant-items.md
267+
[enumerations]: enumerations.md
268+
[functions]: functions.md
146269
[function pointers]: ../types/function-pointer.md
147270
[higher-ranked lifetimes]: ../trait-bounds.md#higher-ranked-trait-bounds
271+
[implementations]: implementations.md
272+
[item]: ../items.md
273+
[literal]: ../expressions/literal-expr.md
274+
[path]: ../paths.md
275+
[path expression]: ../expressions/path-expr.md
148276
[raw pointers]: ../types/pointer.md#raw-pointers-const-and-mut
149277
[references]: ../types/pointer.md#shared-references-
150-
[repeat expressions]: ../expressions/array-expr.md
151278
[`Clone`]: ../special-types-and-traits.md#clone
152279
[`Copy`]: ../special-types-and-traits.md#copy
153280
[`Sized`]: ../special-types-and-traits.md#sized
281+
[structs]: structs.md
154282
[tuples]: ../types/tuple.md
155283
[trait object]: ../types/trait-object.md
284+
[traits]: traits.md
285+
[type aliases]: type-aliases.md
156286
[types]: ../types.md
287+
[unions]: unions.md
157288
[attributes]: ../attributes.md

0 commit comments

Comments
 (0)