Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 605
Add more details about const generics.#921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Uh oh!
There was an error while loading. Please reload this page.
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -17,8 +17,8 @@ | ||
| > _ConstParam_:\ | ||
| > `const` [IDENTIFIER] `:` [_Type_] | ||
| Functions, type aliases, structs, enumerations, unions, traits, and | ||
| implementations may be *parameterized* by types, constants, and lifetimes. These | ||
| [Functions], [type aliases], [structs], [enumerations], [unions], [traits], and | ||
| [implementations] may be *parameterized* by types, constants, and lifetimes. These | ||
| parameters are listed in angle <span class="parenthetical">brackets (`<...>`)</span>, | ||
| usually immediately after the name of the item and before its definition. For | ||
| implementations, which don't have a name, they come directly after `impl`. | ||
| @@ -33,22 +33,147 @@ struct Ref<'a, T> where T: 'a { r: &'a T } | ||
| struct InnerArray<T, const N: usize>([T; N]); | ||
| ``` | ||
| Generic parameters are in scope within the item definition where they are | ||
| declared. They are not in scope for items declared within the body of a | ||
| function as described in [item declarations]. | ||
| [References], [raw pointers], [arrays], [slices][arrays], [tuples], and | ||
| [function pointers] have lifetime or type parameters as well, but are not | ||
| referred to with path syntax. | ||
| ### Const generics | ||
| *Const generic parameters* allow items to be generic over constant values. The | ||
| const identifier introduces a name for the constant parameter, and all | ||
| instances of the item must be instantiated with a value of the given type. | ||
| <!-- TODO: update above to say "introduces a name in the [value namespace]" | ||
| once namespaces are added. --> | ||
| The only allowed types of const parameters are `u8`, `u16`, `u32`, `u64`, `u128`, `usize` | ||
| `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `char` and `bool`. | ||
| Const parameters may only be be used as standalone arguments inside | ||
| of [types] and [repeat expressions] but may be freely used elsewhere: | ||
| 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. | ||
| ```rust | ||
| // Examples where const generic parameters can be used. | ||
| // Used in the signature of the item itself. | ||
| fn foo<const N: usize>(arr: [i32; N]) { | ||
| // Used as a type within a function body. | ||
| let x: [i32; N]; | ||
| // Used as an expression. | ||
| println!("{}", N * 2); | ||
| } | ||
| // Used as a field of a struct. | ||
| struct Foo<const N: usize>([i32; N]); | ||
| impl<const N: usize> Foo<N> { | ||
| // Used as an associated constant. | ||
| const CONST: usize = N * 4; | ||
| } | ||
| trait Trait { | ||
| type Output; | ||
| } | ||
| impl<const N: usize> Trait for Foo<N> { | ||
| // Used as an associated type. | ||
| type Output = [i32; N]; | ||
| } | ||
| ``` | ||
| ```rust,compile_fail | ||
| // Examples where const generic parameters cannot be used. | ||
| fn foo<const N: usize>() { | ||
| // Cannot use in item definitions within a function body. | ||
| const BAD_CONST: [usize; N] = [1; N]; | ||
| static BAD_STATIC: [usize; N] = [1; N]; | ||
| fn inner(bad_arg: [usize; N]) { | ||
| let bad_value = N * 2; | ||
| } | ||
| type BadAlias = [usize; N]; | ||
| struct BadStruct([usize; N]); | ||
| } | ||
| ``` | ||
| 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 | ||
| // ok: standalone argument | ||
| fn foo<const N: usize>() -> [u8; N] { todo!() } | ||
| // Examples where const parameters may not be used. | ||
| // ERROR: generic const operation | ||
| fn bar<const N: usize>() -> [u8; N + 1] { todo!() } | ||
| // Not allowed to combine in other expressions in types, such as the | ||
| // arithmetic expression in the return type here. | ||
| fn bad_function<const N: usize>() -> [u8; {N + 1}] { | ||
| // Similarly not allowed for array repeat expressions. | ||
| [1; {N + 1}] | ||
| } | ||
| ``` | ||
| Unlike type and lifetime parameters, const parameters of types can be used without | ||
| being mentioned inside of a parameterized type: | ||
| A const argument in a [path] specifies the const value to use for that item. | ||
| 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<const N: i32>() { | ||
| 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::<SOME_CONST>(); | ||
| double::<{ SOME_CONST + 5 }>(); | ||
| } | ||
| ``` | ||
| When there is ambiguity if a generic argument could be resolved as either a | ||
Havvy marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 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. | ||
| <!-- 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. --> | ||
| ```rust,compile_fail | ||
| type N = u32; | ||
| struct Foo<const N: usize>; | ||
| // The following is an error, because `N` is interpreted as the type alias `N`. | ||
| fn foo<const N: usize>() -> Foo<N> { todo!() } // ERROR | ||
| // Can be fixed by wrapping in braces to force it to be interprted as the `N` | ||
| // const parameter: | ||
| fn bar<const N: usize>() -> Foo<{ N }> { todo!() } // ok | ||
| ``` | ||
| 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]: | ||
| ```rust,compile_fail | ||
| // ok | ||
| @@ -58,11 +183,29 @@ enum Bar<const M: usize> { A, B } | ||
| // ERROR: unused parameter | ||
| struct Baz<T>; | ||
| struct Biz<'a>; | ||
| struct Unconstrained; | ||
| impl<const N: usize> Unconstrained {} | ||
| ``` | ||
| 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<const B: bool>; | ||
| trait Bar {} | ||
| impl Bar for Foo<true> {} | ||
| impl Bar for Foo<false> {} | ||
| fn needs_bar(_: impl Bar) {} | ||
| fn generic<const B: bool>() { | ||
| let v = Foo::<B>; | ||
| needs_bar(v); // ERROR: trait bound `Foo<B>: Bar` is not satisfied | ||
| } | ||
| ``` | ||
| [References], [raw pointers], [arrays], [slices][arrays], [tuples], and | ||
| [function pointers] have lifetime or type parameters as well, but are not | ||
| referred to with path syntax. | ||
| ## Where clauses | ||
| @@ -90,7 +233,7 @@ parameters. | ||
| The `for` keyword can be used to introduce [higher-ranked lifetimes]. It only | ||
| allows [_LifetimeParam_] parameters. | ||
| Bounds that don't use the item's parameters or higher-ranked lifetimes are | ||
| Bounds that don't use the item's parameters or [higher-ranked lifetimes] are | ||
| checked when the item is defined. It is an error for such a bound to be false. | ||
| [`Copy`], [`Clone`], and [`Sized`] bounds are also checked for certain generic | ||
| @@ -141,17 +284,35 @@ struct Foo<#[my_flexible_clone(unbounded)] H> { | ||
| [_Type_]: ../types.md#type-expressions | ||
| [_TypeParamBounds_]: ../trait-bounds.md | ||
| [array repeat expression]: ../expressions/array-expr.md | ||
| [arrays]: ../types/array.md | ||
| [associated const]: associated-items.md#associated-constants | ||
| [associated type]: associated-items.md#associated-types | ||
| [block]: ../expressions/block-expr.md | ||
| [const contexts]: ../const_eval.md#const-context | ||
| [const expression]: ../const_eval.md#constant-expressions | ||
| [const item]: constant-items.md | ||
| [enumerations]: enumerations.md | ||
| [functions]: functions.md | ||
| [function pointers]: ../types/function-pointer.md | ||
| [generic implementations]: implementations.md#generic-implementations | ||
| [higher-ranked lifetimes]: ../trait-bounds.md#higher-ranked-trait-bounds | ||
| [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 | ||
| [references]: ../types/pointer.md#shared-references- | ||
| [repeat expressions]: ../expressions/array-expr.md | ||
| [`Clone`]: ../special-types-and-traits.md#clone | ||
| [`Copy`]: ../special-types-and-traits.md#copy | ||
| [`Sized`]: ../special-types-and-traits.md#sized | ||
| [structs]: structs.md | ||
| [tuples]: ../types/tuple.md | ||
| [trait object]: ../types/trait-object.md | ||
| [types]: ../types.md | ||
| [traits]: traits.md | ||
| [type aliases]: type-aliases.md | ||
| [type]: ../types.md | ||
| [unions]: unions.md | ||
| [attributes]: ../attributes.md | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was here before, but I have no clue what this sentence is actually stating. Path syntax isn't mentioned at all up to this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduced in #258. If I had to guess, it is maybe trying to say is that the syntax for specifying arguments to those types does not use the same syntax that the other items (structs, fns, etc.) use. That is, instead of IDENT<…> they each have their own unique syntax for specifying arguments.
I agree, it seems a bit confusing. Any suggestions? Maybe @matthewjasper could clarify?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I let my brain think about it overnight. I'm betting this statement was originally in
paths.htmlwhere it makes sense and was moved here as part of a split out. We should probably bring most ofGenericArgumentintogenerics.htmland then renameGenericArgumentstoGenericPathArgumentsor something. I'll open an issue for that one.