diff --git a/src/SUMMARY.md b/src/SUMMARY.md index cbee8c018..0acf77965 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -91,6 +91,7 @@ - [Impl trait type](types/impl-trait.md) - [Type parameters](types/parameters.md) - [Inferred type](types/inferred.md) + - [Const Generics](const-generics.md) - [Dynamically Sized Types](dynamically-sized-types.md) - [Type layout](type-layout.md) - [Interior mutability](interior-mutability.md) diff --git a/src/const-generics.md b/src/const-generics.md new file mode 100644 index 000000000..7bd72d9a2 --- /dev/null +++ b/src/const-generics.md @@ -0,0 +1,174 @@ +r[const-generics] +# Const Generics + +r[const-generics.argument] +A const argument in a [path] specifies the const value to use for that item. + +r[const-generics.argument.type] +The argument must be a [const expression] of the type ascribed to the const +parameter. + +r[items.generics.const.type-ambiguity] +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. + + + +```rust,compile_fail +type N = u32; +struct Foo; +// The following is an error, because `N` is interpreted as the type alias `N`. +fn foo() -> Foo { todo!() } // ERROR +// Can be fixed by wrapping in braces to force it to be interpreted as the `N` +// const parameter: +fn bar() -> Foo<{ N }> { todo!() } // ok +``` + +r[items.generics.const.exhaustiveness] +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: + +```rust,compile_fail +struct Foo; +trait Bar {} +impl Bar for Foo {} +impl Bar for Foo {} + +fn needs_bar(_: impl Bar) {} +fn generic() { + let v = Foo::; + needs_bar(v); // ERROR: trait bound `Foo: Bar` is not satisfied +} +``` + +r[const-generics.kinds] +There are three kinds of arguments to a const parameter: +1. Standalone const parameters +2. Inferred consts +3. Arbitrary concrete expressions + +r[const-generics.standalone] +## Standalone const parameters + +A const parameter can only be used in a const argument if it is a standalone usage. +The argument must be *only* a usage of a const parameter and can be wrapped +in at most one level of braces. That is, they cannot be combined with other +expressions. +```rust +// Examples of standalone uses of const parameters + +fn foo() { + let a: [u8; N] = [10; N]; + let b: [u8; { N }] = a; + foo::(); + foo::<{ N }>(); +} +``` + +Here `a` has type `[u8; N]`, an array with a length of `N`, referring to `foo`'s const parameter. + +```rust,compile_fail +// Examples of non-standalone uses of const parameters + +fn foo() { + let a: [u8; {{ N }}] = [10; (N)]; + foo::<{{ N }}>(); + foo::<(N)>(); +} +``` + +r[const-generics.inferred] +## Inferred consts + +r[const-generics.inferred.syntax] +```grammar,types +@root InferredConst -> `_` +``` + +The inferred const asks the compiler to infer the const if possible based on +the surrounding information available. + +It cannot be used in item signatures. + +It is often used in repeat expressions: +```rust +fn make_array() -> [u32; 2] { + [Default::default(); _] +} +``` + +r[const-generics.concrete-expr] +## Concrete expressions + +Most const expressions are allowed as const arguments: +```rust +// Example of a concrete expressions as an argument + +fn make_array() -> [u8; 1 + 10 / 2] { + [1; 6] +} +``` + +r[const-generics.concrete-expr.limitations] +There are a few limitations about what expressions are allowed: +1. Generic parameters may not be used +2. In-scope where clauses may not be used +3. Must be wrapped in braces in some cases + +```rust,compile_fail +// Examples where expressions may not be used as they use generic parameters. + +// Not allowed in the const argument for an arrays length +fn bad_function() -> [u8; N + 1] { + // Similarly not allowed for array repeat expressions' count argument. + [1; N + 1] +} + +// Using type parameters is also disallowed +fn type_parameters_disallowed(_: [u8; size_of::()]) {} +``` + +```rust,compile_fail +// Example where an expression may not be used as it depends on an in-scope +// where clause + +fn bad_function(_: [u8; { let a: [u8]; 1 }]) +where + for<'a> [u8]: Sized, {} +``` + +The const expression must be a [block expression][block](surrounded with braces) unless it's: +- a single path segment (an [IDENTIFIER]) +- a [literal] (with a possibly leading `-` token) +- an array length or repeat expression count + +> [!NOTE] +> This syntactic restriction is necessary to avoid requiring infinite lookahead when parsing an expression inside of a type. + +```rust +fn double() { + println!("doubled: {}", N * 2); +} + +const SOME_CONST: i32 = 12; + +fn example() { + // Example usage of a const argument. + double::<9>(); + double::<-123>(); + double::<{7 + 8}>(); + double::(); + double::<{ SOME_CONST + 5 }>(); +} +``` + +[block]: ../expressions/block-expr.md +[const expression]: ../const_eval.md#constant-expressions +[literal]: ../expressions/literal-expr.md +[path]: ../paths.md \ No newline at end of file diff --git a/src/items/generics.md b/src/items/generics.md index 3a2e98a0a..5061c7088 100644 --- a/src/items/generics.md +++ b/src/items/generics.md @@ -67,36 +67,26 @@ The only allowed types of const parameters are `u8`, `u16`, `u32`, `u64`, `u128` `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `char` and `bool`. r[items.generics.const.usage] -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: - -1. As an applied const to any type which forms a part of the signature of the - item in question. -2. As part of a const expression used to define an [associated const], or as a - parameter to an [associated type]. -3. As a value in any runtime expression in the body of any functions in the - item. -4. As a parameter to any type used in the body of any functions in the item. -5. As a part of the type of any fields in the item. +Const parameters can be used anywhere in expression position, or as a const argument[^1]. +Uses of const parameters must take place inside the item defining it, and not within +an inner item that inhibits access to outer generic parameters. ```rust // Examples where const generic parameters can be used. // Used in the signature of the item itself. fn foo(arr: [i32; N]) { - // Used as a type within a function body. + // Used within a type within a function body. let x: [i32; N]; - // Used as an expression. + // Used within an expression. println!("{}", N * 2); } -// Used as a field of a struct. +// Used within a field of a struct. struct Foo([i32; N]); impl Foo { - // Used as an associated constant. + // Used within an associated constant. const CONST: usize = N * 4; } @@ -105,13 +95,14 @@ trait Trait { } impl Trait for Foo { - // Used as an associated type. + // Used within an associated type. type Output = [i32; N]; } ``` ```rust,compile_fail -// Examples where const generic parameters cannot be used. +// Examples where const generic parameters cannot be used as the uses are +// within an inner item that inhibits access to outer generic parameters. fn foo() { // Cannot use in item definitions within a function body. const BAD_CONST: [usize; N] = [1; N]; @@ -124,72 +115,6 @@ fn foo() { } ``` -r[items.generics.const.standalone] -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. - -```rust,compile_fail -// Examples where const parameters may not be used. - -// Not allowed to combine in other expressions in types, such as the -// arithmetic expression in the return type here. -fn bad_function() -> [u8; {N + 1}] { - // Similarly not allowed for array repeat expressions. - [1; {N + 1}] -} -``` - -r[items.generics.const.argument] -A const argument in a [path] specifies the const value to use for that item. - -r[items.generics.const.argument.const-expr] -The argument must be a [const expression] of the type ascribed to the const -parameter. The const expression must be a [block expression][block] -(surrounded with braces) unless it is a single path segment (an [IDENTIFIER]) -or a [literal] (with a possibly leading `-` token). - -> [!NOTE] -> This syntactic restriction is necessary to avoid requiring infinite lookahead when parsing an expression inside of a type. - -```rust -fn double() { - println!("doubled: {}", N * 2); -} - -const SOME_CONST: i32 = 12; - -fn example() { - // Example usage of a const argument. - double::<9>(); - double::<-123>(); - double::<{7 + 8}>(); - double::(); - double::<{ SOME_CONST + 5 }>(); -} -``` - -r[items.generics.const.type-ambiguity] -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. - - - -```rust,compile_fail -type N = u32; -struct Foo; -// The following is an error, because `N` is interpreted as the type alias `N`. -fn foo() -> Foo { todo!() } // ERROR -// Can be fixed by wrapping in braces to force it to be interpreted as the `N` -// const parameter: -fn bar() -> Foo<{ N }> { todo!() } // ok -``` - r[items.generics.const.variance] Unlike type and lifetime parameters, const parameters can be declared without being used inside of a parameterized item, with the exception of @@ -207,26 +132,6 @@ struct Unconstrained; impl Unconstrained {} ``` -r[items.generics.const.exhaustiveness] -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: - -```rust,compile_fail -struct Foo; -trait Bar {} -impl Bar for Foo {} -impl Bar for Foo {} - -fn needs_bar(_: impl Bar) {} -fn generic() { - let v = Foo::; - needs_bar(v); // ERROR: trait bound `Foo: Bar` is not satisfied -} -``` - r[items.generics.where] ## Where clauses @@ -284,16 +189,18 @@ struct Foo<#[my_flexible_clone(unbounded)] H> { } ``` +[^1]: There are limitations around how a const parameter can be used within a const argument ([const generics]). + [array repeat expression]: ../expressions/array-expr.md [arrays]: ../types/array.md [slices]: ../types/slice.md [associated const]: associated-items.md#associated-constants [associated type]: associated-items.md#associated-types [attributes]: ../attributes.md -[block]: ../expressions/block-expr.md [const contexts]: ../const_eval.md#const-context [const expression]: ../const_eval.md#constant-expressions [const item]: constant-items.md +[const generics]: ../const-generics.md [enumerations]: enumerations.md [functions]: functions.md [function pointers]: ../types/function-pointer.md @@ -303,7 +210,6 @@ struct Foo<#[my_flexible_clone(unbounded)] H> { [implementations]: implementations.md [item declarations]: ../statements.md#item-declarations [item]: ../items.md -[literal]: ../expressions/literal-expr.md [path]: ../paths.md [path expression]: ../expressions/path-expr.md [raw pointers]: ../types/pointer.md#raw-pointers-const-and-mut @@ -315,4 +221,4 @@ struct Foo<#[my_flexible_clone(unbounded)] H> { [type aliases]: type-aliases.md [type]: ../types.md [unions]: unions.md -[value namespace]: ../names/namespaces.md +[value namespace]: ../names/namespaces.md \ No newline at end of file diff --git a/src/patterns.md b/src/patterns.md index 60cfb21c4..a71c2af17 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -973,7 +973,8 @@ r[patterns.const.exhaustive] r[patterns.const.generic] In particular, the value of `C` must be known at pattern-building time (which is pre-monomorphization). -This means that associated consts that involve generic parameters cannot be used as patterns. +This means that associated consts that involve generic parameters cannot be used as patterns and neither +can uses of const parameters. r[patterns.const.translation] 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.