Skip to content

Commit 84a0a7a

Browse files
committed
Add more details about const generics.
1 parent c81cd07 commit 84a0a7a

File tree

1 file changed

+146
-14
lines changed

1 file changed

+146
-14
lines changed

src/items/generics.md

+146-14
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
> _ConstParam_:\
2828
> &nbsp;&nbsp; [_OuterAttribute_]<sup>?</sup> `const` [IDENTIFIER] `:` [_Type_]
2929
30-
Functions, type aliases, structs, enumerations, unions, traits, and
31-
implementations may be *parameterized* by types, constants, and lifetimes. These
30+
[Functions], [type aliases], [structs], [enumerations], [unions], [traits], and
31+
[implementations] may be *parameterized* by types, constants, and lifetimes. These
3232
parameters are listed in angle <span class="parenthetical">brackets (`<...>`)</span>,
3333
usually immediately after the name of the item and before its definition. For
3434
implementations, which don't have a name, they come directly after `impl`.
@@ -43,18 +43,137 @@ struct Ref<'a, T> where T: 'a { r: &'a T }
4343
struct InnerArray<T, const N: usize>([T; N]);
4444
```
4545

46+
The generic parameters are in scope within the item definition where they are
47+
declared.
48+
49+
[References], [raw pointers], [arrays], [slices][arrays], [tuples], and
50+
[function pointers] have lifetime or type parameters as well, but are not
51+
referred to with path syntax.
52+
53+
### Const generics
54+
55+
Const generic parameters allow items to be generic over constant values. The
56+
const identifier introduces a name for the constant parameter, and all
57+
instances of the item must be instantiated with a value of the given type.
58+
59+
<!-- TODO: update above to say "introduces a name in the [value namespace]"
60+
once namespaces are added. -->
61+
4662
The only allowed types of const parameters are `u8`, `u16`, `u32`, `u64`, `u128`, `usize`
4763
`i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `char` and `bool`.
4864

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

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

56-
// ERROR: generic const operation
57-
fn bar<const N: usize>() -> [u8; N + 1] { todo!() }
153+
const SOME_CONST: i32 = 12;
154+
155+
fn example() {
156+
// Example usage of a const argument.
157+
double::<9>();
158+
double::<-123>();
159+
double::<{7 + 8}>();
160+
double::<SOME_CONST>();
161+
double::<{ SOME_CONST + 5 }>();
162+
}
163+
```
164+
165+
When there is ambiguity if a generic argument could be resolved as either a
166+
type or const argument, it is always resolved as a type. Placing the argument
167+
in a block expression can force it to be interpreted as a const argument.
168+
169+
```rust,compile_fail
170+
type N = u32;
171+
struct Foo<const N: usize>;
172+
// The following is an error, because `N` is interpreted as the type alias `N`.
173+
fn foo<const N: usize>() -> Foo<N> { todo!() } // ERROR
174+
// Can be fixed by wrapping in braces to force it to be interprted as the `N`
175+
// const parameter:
176+
fn bar<const N: usize>() -> Foo<{ N }> { todo!() } // ok
58177
```
59178

60179
Unlike type and lifetime parameters, const parameters of types can be used without
@@ -70,10 +189,6 @@ struct Baz<T>;
70189
struct Biz<'a>;
71190
```
72191

73-
[References], [raw pointers], [arrays], [slices][arrays], [tuples], and
74-
[function pointers] have lifetime or type parameters as well, but are not
75-
referred to with path syntax.
76-
77192
## Where clauses
78193

79194
> **<sup>Syntax</sup>**\
@@ -97,7 +212,7 @@ referred to with path syntax.
97212
parameters as well as a way to specify bounds on types that aren't type
98213
parameters.
99214

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

103218
[`Copy`], [`Clone`], and [`Sized`] bounds are also checked for certain generic
@@ -147,16 +262,33 @@ struct Foo<#[my_flexible_clone(unbounded)] H> {
147262
[_Type_]: ../types.md#type-expressions
148263
[_TypeParamBounds_]: ../trait-bounds.md
149264

265+
[array repeat expressions]: ../expressions/array-expr.md
150266
[arrays]: ../types/array.md
267+
[associated const]: associated-items.md#associated-constants
268+
[associated type]: associated-items.md#associated-types
269+
[block]: ../expressions/block-expr.md
151270
[const contexts]: ../const_eval.md#const-context
271+
[const expression]: ../const_eval.md#constant-expressions
272+
[const item]: constant-items.md
273+
[enumerations]: enumerations.md
274+
[functions]: functions.md
152275
[function pointers]: ../types/function-pointer.md
276+
[higher-ranked lifetimes]: ../trait-bounds.md#higher-ranked-trait-bounds
277+
[implementations]: implementations.md
278+
[item]: ../items.md
279+
[literal]: ../expressions/literal-expr.md
280+
[path]: ../paths.md
281+
[path expression]: ../expressions/path-expr.md
153282
[raw pointers]: ../types/pointer.md#raw-pointers-const-and-mut
154283
[references]: ../types/pointer.md#shared-references-
155-
[repeat expressions]: ../expressions/array-expr.md
156284
[`Clone`]: ../special-types-and-traits.md#clone
157285
[`Copy`]: ../special-types-and-traits.md#copy
158286
[`Sized`]: ../special-types-and-traits.md#sized
287+
[structs]: structs.md
159288
[tuples]: ../types/tuple.md
160289
[trait object]: ../types/trait-object.md
290+
[traits]: traits.md
291+
[type aliases]: type-aliases.md
161292
[types]: ../types.md
293+
[unions]: unions.md
162294
[attributes]: ../attributes.md

0 commit comments

Comments
 (0)