diff --git a/src/items/associated-items.md b/src/items/associated-items.md index f5dc31aae..2401127b5 100644 --- a/src/items/associated-items.md +++ b/src/items/associated-items.md @@ -205,22 +205,49 @@ 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 +that the associated type must meet, and `WhereBounds` is a comma-separated list +of bounds that the parameters must meet: + + +```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 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 = 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 `::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 must not include [generic parameters] or [where clauses]. +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 trait AssociatedType { @@ -249,6 +276,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 @@ -279,6 +337,83 @@ 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 +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 +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<'_>); +} +``` + +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. 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 ff0cc75f7..d2e14b903 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 @@ -31,11 +31,18 @@ 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 [_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], must not include a +[_Type_] specification but may include [_TypeParamBounds_]. + +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 +the equals sign (like `type TypeAlias = Bar where T: Foo`) are preferred. [IDENTIFIER]: ../identifiers.md [_GenericParams_]: generics.md @@ -45,3 +52,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