-
Notifications
You must be signed in to change notification settings - Fork 492
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
Add basic GATs reference information #1265
Changes from 6 commits
60cd80a
5e8efd6
1ef107a
a7a54f5
dd84a62
dc1eeb4
0763b2e
3970ed3
37881f5
3c8acda
6b9e4ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -205,22 +205,47 @@ 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 | ||
jackh726 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
type Assoc; | ||
type Assoc: Bounds; | ||
type Assoc<Params>; | ||
type Assoc<Params>: Bounds; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It occurs to me that it would be worth clarifying the distinction between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is stated in the paragraph above? Should we be more detailed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could be more explicit. Left some suggestions. WDYT? |
||
type Assoc<Params> where WhereBounds; | ||
type Assoc<Params>: Bounds where WhereBounds; | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe something like Relationship between
|
||
|
||
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 | ||
jackh726 marked this conversation as resolved.
Show resolved
Hide resolved
nikomatsakis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
jackh726 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
type Assoc = Type; | ||
type Assoc<Params> = Type<Params>; | ||
nikomatsakis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
type Assoc<Params> where WhereBounds = Type; | ||
type Assoc<Params> = Type where WhereBounds; | ||
jackh726 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
If a type `Item` has an associated type `Assoc` from a trait `Trait`, then | ||
`<Item as Trait>::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 `<Thing as Trait>::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 +274,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<T> 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) -> <T as Lend>::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 +335,68 @@ impl<T> Container for Vec<T> { | |
} | ||
``` | ||
|
||
### 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. | ||
nikomatsakis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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<T> { | ||
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<Item = Self::Item<'a>> 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. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
> _TypeAlias_ :\ | ||
> `type` [IDENTIFIER] [_GenericParams_]<sup>?</sup> | ||
> ( `:` [_TypeParamBounds_] )<sup>?</sup> | ||
> [_WhereClause_]<sup>?</sup> ( `=` [_Type_] )<sup>?</sup> `;` | ||
> [_WhereClause_]<sup>?</sup> ( `=` [_Type_] [_WhereClause_]<sup>?</sup>)<sup>?</sup> `;` | ||
|
||
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,9 +34,16 @@ 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]. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This clause by itself doesn't seem to be correct to me. It seems to imply that the following is not allowed: type X = i32; Perhaps this could be turned around so that it specifically says what a trait impl associated type needs? A sentence could be added to the previous paragraph, since they are talking about the same thing. Maybe something like this: A type alias without the [Type] specification may only appear as an [associated type] in a [trait]. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've made a separate paragraph for each for of type aliases, to be specific about what is allow, what is required, and what isn't allowed in each. |
||
|
||
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 | ||
jackh726 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
`type TypeAlias<T> where T: Foo = Bar<T>`) are deprecated. Where clauses after | ||
the equals sign (like `type TypeAlias<T> = Bar<T> where T: Foo`) are preferred. | ||
|
||
[IDENTIFIER]: ../identifiers.md | ||
[_GenericParams_]: generics.md | ||
[_TypeParamBounds_]: ../trait-bounds.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 |
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.