From 60cd80a6732f8375e8f5303290f27b97e33a0263 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Thu, 8 Sep 2022 20:45:12 -0400 Subject: [PATCH 01/11] Add basic GATs info --- src/items/associated-items.md | 38 ++++++++++++++++++++++++++++++++++- src/items/type-aliases.md | 6 +++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/items/associated-items.md b/src/items/associated-items.md index f5dc31aae..66db45b54 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -220,7 +220,12 @@ If a type `Item` has an associated type `Assoc` from a trait `Trait`, then associated type definition. Furthermore, if `Item` is a type parameter, then `Item::Assoc` can be used in type parameters. -Associated types must not include [generic parameters] or [where clauses]. +Associated types may include [generic parameters] or [where clauses]; these may +be referred to generic associated types, or GATs. If the type `Thing` has an +associated type `Item` from a trait `Trait` with the generics `<'a>` , the type +can be named like `::Item<'x>`, where `'x` is some lifetime in +scope. In this case, `'x` will be used wherever `'a` appears in the associated +type definitions on impls. ```rust trait AssociatedType { @@ -249,6 +254,37 @@ fn main() { } ``` +An example of associated types with generics and where clauses: + +```rust +struct ArrayLender<'a, T>(&'a mut [T; 16]); + +trait Lend { + // Generic associated type declaration + type Lender<'a> where Self: 'a; + fn lend<'a>(&'a mut self) -> Self::Lender<'a>; +} + +impl Lend for [T; 16] { + // Generic associated type definition + type Lender<'a> = ArrayLender<'a, T> where Self: 'a; + + fn lend<'a>(&'a mut self) -> Self::Lender<'a> { + ArrayLender(self) + } +} + +fn borrow<'a, T: Lend>(array: &'a mut T) -> ::Lender<'a> { + array.lend() +} + + +fn main() { + let mut array = [0usize; 16]; + let lender = borrow(&mut array); +} +``` + ### Associated Types Container Example Consider the following example of a `Container` trait. Notice that the type is diff --git a/src/items/type-aliases.md b/src/items/type-aliases.md index ff0cc75f7..2b5307600 100644 --- a/src/items/type-aliases.md +++ b/src/items/type-aliases.md @@ -4,7 +4,7 @@ > _TypeAlias_ :\ >    `type` [IDENTIFIER] [_GenericParams_]? > ( `:` [_TypeParamBounds_] )? -> [_WhereClause_]? ( `=` [_Type_] )? `;` +> [_WhereClause_]? ( `=` [_Type_] )? [_WhereClause_]? `;` A _type alias_ defines a new name for an existing [type]. Type aliases are declared with the keyword `type`. Every value has a single, specific type, but @@ -37,6 +37,9 @@ A type alias without the [_Type_] specification may only appear as an A type alias with [_TypeParamBounds_] may only specified when used as an [associated type] in a [trait]. +A type alias with where clauses after the equals sign may only appear as an +[associated type] in a [trait] or a [trait impl]. + [IDENTIFIER]: ../identifiers.md [_GenericParams_]: generics.md [_TypeParamBounds_]: ../trait-bounds.md @@ -45,3 +48,4 @@ an [associated type] in a [trait]. [associated type]: associated-items.md#associated-types [trait]: traits.md [type]: ../types.md +[trait impl]: implementations.md#trait-implementations From 5e8efd6fdaa4914d482c81226ee8e7f6f2b194ca Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Thu, 8 Sep 2022 21:47:36 -0400 Subject: [PATCH 02/11] Fix typo --- src/items/associated-items.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/associated-items.md b/src/items/associated-items.md index 66db45b54..48f0eca5b 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -221,7 +221,7 @@ associated type definition. Furthermore, if `Item` is a type parameter, then `Item::Assoc` can be used in type parameters. Associated types may include [generic parameters] or [where clauses]; these may -be referred to generic associated types, or GATs. If the type `Thing` has an +be referred to as generic associated types, or GATs. If the type `Thing` has an associated type `Item` from a trait `Trait` with the generics `<'a>` , the type can be named like `::Item<'x>`, where `'x` is some lifetime in scope. In this case, `'x` will be used wherever `'a` appears in the associated From 1ef107a457a2d2945887fc026a28ee557710918b Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 9 Sep 2022 19:57:12 -0400 Subject: [PATCH 03/11] Review comments --- src/items/associated-items.md | 38 ++++++++++++++++++++++++++--------- src/items/traits.md | 1 + src/items/type-aliases.md | 10 ++++++--- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/items/associated-items.md b/src/items/associated-items.md index 48f0eca5b..009ccd0ba 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -205,26 +205,46 @@ types cannot be defined in [inherent implementations] nor can they be given a default implementation in traits. An *associated type declaration* declares a signature for associated type -definitions. It is written as `type`, then an [identifier], and -finally an optional list of trait bounds. +definitions. It is written in one of the following forms, where `Assoc` is the +name of the associated type, `Params` is a comma-separated list of type, +lifetime or const parameters, `Bounds` is a plus-separated list of trait bounds +on the associated type, and `WhereBounds` is a comma-separated list of bounds on +parameters: + +```rust,ignore +type Assoc; +type Assoc: Bounds; +type Assoc; +type Assoc: Bounds; +type Assoc where WhereBounds; +type Assoc: Bounds where WhereBounds; +``` The identifier is the name of the declared type alias. The optional trait bounds must be fulfilled by the implementations of the type alias. There is an implicit [`Sized`] bound on associated types that can be relaxed using the special `?Sized` bound. -An *associated type definition* defines a type alias on another type. It is -written as `type`, then an [identifier], then an `=`, and finally a [type]. +An *associated type definition* defines a type alias on for the implementation +of a trait on a type. They are written similarly to an *associated type declaration*, +but cannot contain `Bounds`, but instead must contain a `Type`: + +```rust,ignore +type Assoc = Type; +type Assoc = Type; +type Assoc where WhereBounds = Type; +type Assoc = Type where WhereBounds; +``` If a type `Item` has an associated type `Assoc` from a trait `Trait`, then `::Assoc` is a type that is an alias of the type specified in the associated type definition. Furthermore, if `Item` is a type parameter, then `Item::Assoc` can be used in type parameters. -Associated types may include [generic parameters] or [where clauses]; these may -be referred to as generic associated types, or GATs. If the type `Thing` has an -associated type `Item` from a trait `Trait` with the generics `<'a>` , the type -can be named like `::Item<'x>`, where `'x` is some lifetime in -scope. In this case, `'x` will be used wherever `'a` appears in the associated +Associated types may include [generic parameters] and [where clauses]; these are +often referred to as *generic associated types*, or *GATs*. If the type `Thing` +has an associated type `Item` from a trait `Trait` with the generics `<'a>` , the +type can be named like `::Item<'x>`, where `'x` is some lifetime +in scope. In this case, `'x` will be used wherever `'a` appears in the associated type definitions on impls. ```rust diff --git a/src/items/traits.md b/src/items/traits.md index 26870a0fc..3c5d31d5c 100644 --- a/src/items/traits.md +++ b/src/items/traits.md @@ -70,6 +70,7 @@ Object safe traits can be the base trait of a [trait object]. A trait is * All [supertraits] must also be object safe. * `Sized` must not be a [supertrait][supertraits]. In other words, it must not require `Self: Sized`. * It must not have any associated constants. +* It must not have any associated types with generics. * All associated functions must either be dispatchable from a trait object or be explicitly non-dispatchable: * Dispatchable functions require: * Not have any type parameters (although lifetime parameters are allowed), diff --git a/src/items/type-aliases.md b/src/items/type-aliases.md index 2b5307600..74463ea78 100644 --- a/src/items/type-aliases.md +++ b/src/items/type-aliases.md @@ -4,7 +4,7 @@ > _TypeAlias_ :\ >    `type` [IDENTIFIER] [_GenericParams_]? > ( `:` [_TypeParamBounds_] )? -> [_WhereClause_]? ( `=` [_Type_] )? [_WhereClause_]? `;` +> [_WhereClause_]? ( `=` [_Type_] [_WhereClause_]?)? `;` A _type alias_ defines a new name for an existing [type]. Type aliases are declared with the keyword `type`. Every value has a single, specific type, but @@ -34,11 +34,15 @@ let _ = TypeAlias(5); // Doesn't work A type alias without the [_Type_] specification may only appear as an [associated type] in a [trait]. +A type alias with the [_Type_] specification may only appear as an +[associated type] in a [trait impl]. + A type alias with [_TypeParamBounds_] may only specified when used as an [associated type] in a [trait]. -A type alias with where clauses after the equals sign may only appear as an -[associated type] in a [trait] or a [trait impl]. +Where clauses before the equals sign on a type alias in a [trait impl] (like +`type TypeAlias where T: Foo = Bar`) are deprecated. Where clauses after +the equals sign (like `type TypeAlias where T: Foo = Bar`) are preferred. [IDENTIFIER]: ../identifiers.md [_GenericParams_]: generics.md From a7a54f5ee340db8477cff9f17cbaea5d78e42ad3 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:21:18 -0400 Subject: [PATCH 04/11] Add explanation for required clauses on GATs --- src/items/associated-items.md | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/items/associated-items.md b/src/items/associated-items.md index 009ccd0ba..33eb4a7a9 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -335,6 +335,68 @@ impl Container for Vec { } ``` +### Required where clauses on generic associated types + +Generic associated type declarations on traits currently may require a list of +where clauses, dependent on functions in the trait and how the GAT is used. + +In a few words, these where clauses are required in order to maximize the allowed +definitions of the associated type in impls. To do this, any clauses that *can be +proven to hold* on functions (using the parameters of the function or trait) +where a GAT appears as an input or output must also be written on the GAT itself. + +```rust +trait LendingIterator { + type Item<'x> where Self: 'x; + fn next<'a>(&'a mut self) -> Self::Item<'a>; +} +``` + +In the above, on the `next` function, we can prove that `Self: 'a`, because of +the implied bounds from `&'a mut self`; therefore, we must write the equivalent +bound on the GAT itself: `where Self: 'x`. + +When there are multiple functions in a trait that use the GAT, then the +*intersection* of the bounds from the different functions are used, rather than +the union. + +```rust +trait Check { + type Checker<'x>; + fn create_checker<'a>(item: &'a T) -> Self::Checker<'a>; + fn do_check(checker: Self::Checker<'a>); +} +``` + +In this example, no bounds are required on the `type Checker<'a>;`. While we +know that `T: 'a` on `create_checker`, we do not know that on `do_check`. However, +if `do_check` was commented out, then the `where T: 'x` bound would be required +on `Checker`. + +The bounds on associated types also propagate required where clauses. + +```rust +trait Iterable { + type Item<'a> where Self: 'a; + type Iterator<'a>: Iterator> where Self: 'a; + fn iter<'a>(&'a self) -> Self::Iterator<'a>; +} +``` + +Here, `where Self: 'a` is required on `Item` because of `iter`. However, `Item` +is used in the bounds of `Iterator`, the `where Self: 'a` clause is also required +there. + +Finally, any explicit uses of `'static` on GATs in the trait do not count towards +the required bounds. + +```rust +trait StaticReturn { + type Y<'a>; + fn foo(&self) -> Self::Y<'static>; +} +``` + ## Associated Constants *Associated constants* are [constants] associated with a type. From dd84a6268eaade7e59460fac51d885685825c777 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:26:02 -0400 Subject: [PATCH 05/11] Fixed second where clause location --- src/items/type-aliases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/type-aliases.md b/src/items/type-aliases.md index 74463ea78..dd0efee04 100644 --- a/src/items/type-aliases.md +++ b/src/items/type-aliases.md @@ -42,7 +42,7 @@ an [associated type] in a [trait]. Where clauses before the equals sign on a type alias in a [trait impl] (like `type TypeAlias where T: Foo = Bar`) are deprecated. Where clauses after -the equals sign (like `type TypeAlias where T: Foo = Bar`) are preferred. +the equals sign (like `type TypeAlias = Bar where T: Foo`) are preferred. [IDENTIFIER]: ../identifiers.md [_GenericParams_]: generics.md From dc1eeb4cbb64c3f8fdc17635330bb533992d2bef Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 11 Sep 2022 21:55:36 -0400 Subject: [PATCH 06/11] Fix lifetime --- src/items/associated-items.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/associated-items.md b/src/items/associated-items.md index 33eb4a7a9..d28ab6b13 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -364,7 +364,7 @@ the union. trait Check { type Checker<'x>; fn create_checker<'a>(item: &'a T) -> Self::Checker<'a>; - fn do_check(checker: Self::Checker<'a>); + fn do_check(checker: Self::Checker<'_>); } ``` From 0763b2ebb6c8d4c8057f1a498438d791840495f7 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 16 Sep 2022 10:10:46 -0400 Subject: [PATCH 07/11] Nikos review --- src/items/associated-items.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/items/associated-items.md b/src/items/associated-items.md index d28ab6b13..97b2c2087 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -230,7 +230,7 @@ but cannot contain `Bounds`, but instead must contain a `Type`: ```rust,ignore type Assoc = Type; -type Assoc = Type; +type Assoc = Type; // the type `Type` here may reference `Params` type Assoc where WhereBounds = Type; type Assoc = Type where WhereBounds; ``` @@ -338,7 +338,9 @@ impl Container for Vec { ### Required where clauses on generic associated types Generic associated type declarations on traits currently may require a list of -where clauses, dependent on functions in the trait and how the GAT is used. +where clauses, dependent on functions in the trait and how the GAT is used. These +rules may be loosened in the future; updates can be found [on the generic +associated types initiative repository](https://rust-lang.github.io/generic-associated-types-initiative/explainer/required_bounds.html). In a few words, these where clauses are required in order to maximize the allowed definitions of the associated type in impls. To do this, any clauses that *can be From 3970ed35ebbb91083dfadceea35ae4c982969a83 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 16 Sep 2022 10:20:59 -0400 Subject: [PATCH 08/11] Remove space --- src/items/type-aliases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/type-aliases.md b/src/items/type-aliases.md index dd0efee04..5ab885af8 100644 --- a/src/items/type-aliases.md +++ b/src/items/type-aliases.md @@ -40,7 +40,7 @@ A type alias with the [_Type_] specification may only appear as an A type alias with [_TypeParamBounds_] may only specified when used as an [associated type] in a [trait]. -Where clauses before the equals sign on a type alias in a [trait impl] (like +Where clauses before the equals sign on a type alias in a [trait impl] (like `type TypeAlias where T: Foo = Bar`) are deprecated. Where clauses after the equals sign (like `type TypeAlias = Bar where T: Foo`) are preferred. From 37881f526811bdf0d27a47da19bb725fb671128b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 18 Sep 2022 06:20:15 -0400 Subject: [PATCH 09/11] Update src/items/associated-items.md --- src/items/associated-items.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/associated-items.md b/src/items/associated-items.md index 97b2c2087..4996ff719 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -224,7 +224,7 @@ The identifier is the name of the declared type alias. The optional trait bounds must be fulfilled by the implementations of the type alias. There is an implicit [`Sized`] bound on associated types that can be relaxed using the special `?Sized` bound. -An *associated type definition* defines a type alias on for the implementation +An *associated type definition* defines a type alias for the implementation of a trait on a type. They are written similarly to an *associated type declaration*, but cannot contain `Bounds`, but instead must contain a `Type`: From 3c8acda52ffcf5f7ca410c76f4fddf0e129592e2 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Wed, 28 Sep 2022 18:11:43 -0400 Subject: [PATCH 10/11] Review comments --- src/items/associated-items.md | 4 +++- src/items/type-aliases.md | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/items/associated-items.md b/src/items/associated-items.md index 4996ff719..0bc1a2b8c 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -211,6 +211,7 @@ lifetime or const parameters, `Bounds` is a plus-separated list of trait bounds on the associated type, and `WhereBounds` is a comma-separated list of bounds on parameters: + ```rust,ignore type Assoc; type Assoc: Bounds; @@ -228,11 +229,12 @@ An *associated type definition* defines a type alias for the implementation of a trait on a type. They are written similarly to an *associated type declaration*, but cannot contain `Bounds`, but instead must contain a `Type`: + ```rust,ignore type Assoc = Type; type Assoc = Type; // the type `Type` here may reference `Params` -type Assoc where WhereBounds = Type; type Assoc = Type where WhereBounds; +type Assoc where WhereBounds = Type; // deprecated, prefer the form above ``` If a type `Item` has an associated type `Assoc` from a trait `Trait`, then diff --git a/src/items/type-aliases.md b/src/items/type-aliases.md index 5ab885af8..d2e14b903 100644 --- a/src/items/type-aliases.md +++ b/src/items/type-aliases.md @@ -31,14 +31,14 @@ let _ = UseAlias(5); // OK let _ = TypeAlias(5); // Doesn't work ``` -A type alias without the [_Type_] specification may only appear as an -[associated type] in a [trait]. +A type alias, when not used as an associated type, must include a [_Type_] and +may not include [_TypeParamBounds_]. -A type alias with the [_Type_] specification may only appear as an -[associated type] in a [trait impl]. +A type alias, when used as an [associated type] in a [trait], must not include a +[_Type_] specification but may include [_TypeParamBounds_]. -A type alias with [_TypeParamBounds_] may only specified when used as -an [associated type] in a [trait]. +A type alias, when used as an [associated type] in a [trait impl], must include +a [_Type_] specification and may not include [_TypeParamBounds_]. Where clauses before the equals sign on a type alias in a [trait impl] (like `type TypeAlias where T: Foo = Bar`) are deprecated. Where clauses after From 6b9e4ffd539af7db5e27b6c829f120f827ef69a4 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Tue, 18 Oct 2022 20:14:44 -0400 Subject: [PATCH 11/11] Add a section on relationship between bounds and wherebounds --- src/items/associated-items.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/items/associated-items.md b/src/items/associated-items.md index 0bc1a2b8c..2401127b5 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -208,8 +208,8 @@ An *associated type declaration* declares a signature for associated type definitions. It is written in one of the following forms, where `Assoc` is the name of the associated type, `Params` is a comma-separated list of type, lifetime or const parameters, `Bounds` is a plus-separated list of trait bounds -on the associated type, and `WhereBounds` is a comma-separated list of bounds on -parameters: +that the associated type must meet, and `WhereBounds` is a comma-separated list +of bounds that the parameters must meet: ```rust,ignore @@ -337,6 +337,19 @@ impl Container for Vec { } ``` +### Relationship between `Bounds` and `WhereBounds` + +In this example: + +```rust +# use std::fmt::Debug; +trait Example { + type Output: Ord where T: Debug; +} +``` + +Given a reference to the associated type like `::Output`, the associated type itself must be `Ord`, and the type `Y` must be `Debug`. + ### Required where clauses on generic associated types Generic associated type declarations on traits currently may require a list of