Skip to content

Commit 8104645

Browse files
authored
Merge pull request #1265 from jackh726/gats
Add basic GATs reference information
2 parents f6ed74f + 6b9e4ff commit 8104645

File tree

3 files changed

+154
-10
lines changed

3 files changed

+154
-10
lines changed

src/items/associated-items.md

+140-5
Original file line numberDiff line numberDiff line change
@@ -205,22 +205,49 @@ types cannot be defined in [inherent implementations] nor can they be given a
205205
default implementation in traits.
206206

207207
An *associated type declaration* declares a signature for associated type
208-
definitions. It is written as `type`, then an [identifier], and
209-
finally an optional list of trait bounds.
208+
definitions. It is written in one of the following forms, where `Assoc` is the
209+
name of the associated type, `Params` is a comma-separated list of type,
210+
lifetime or const parameters, `Bounds` is a plus-separated list of trait bounds
211+
that the associated type must meet, and `WhereBounds` is a comma-separated list
212+
of bounds that the parameters must meet:
213+
214+
<!-- ignore: illustrative example forms -->
215+
```rust,ignore
216+
type Assoc;
217+
type Assoc: Bounds;
218+
type Assoc<Params>;
219+
type Assoc<Params>: Bounds;
220+
type Assoc<Params> where WhereBounds;
221+
type Assoc<Params>: Bounds where WhereBounds;
222+
```
210223

211224
The identifier is the name of the declared type alias. The optional trait bounds
212225
must be fulfilled by the implementations of the type alias.
213226
There is an implicit [`Sized`] bound on associated types that can be relaxed using the special `?Sized` bound.
214227

215-
An *associated type definition* defines a type alias on another type. It is
216-
written as `type`, then an [identifier], then an `=`, and finally a [type].
228+
An *associated type definition* defines a type alias for the implementation
229+
of a trait on a type. They are written similarly to an *associated type declaration*,
230+
but cannot contain `Bounds`, but instead must contain a `Type`:
231+
232+
<!-- ignore: illustrative example forms -->
233+
```rust,ignore
234+
type Assoc = Type;
235+
type Assoc<Params> = Type; // the type `Type` here may reference `Params`
236+
type Assoc<Params> = Type where WhereBounds;
237+
type Assoc<Params> where WhereBounds = Type; // deprecated, prefer the form above
238+
```
217239

218240
If a type `Item` has an associated type `Assoc` from a trait `Trait`, then
219241
`<Item as Trait>::Assoc` is a type that is an alias of the type specified in the
220242
associated type definition. Furthermore, if `Item` is a type parameter, then
221243
`Item::Assoc` can be used in type parameters.
222244

223-
Associated types must not include [generic parameters] or [where clauses].
245+
Associated types may include [generic parameters] and [where clauses]; these are
246+
often referred to as *generic associated types*, or *GATs*. If the type `Thing`
247+
has an associated type `Item` from a trait `Trait` with the generics `<'a>` , the
248+
type can be named like `<Thing as Trait>::Item<'x>`, where `'x` is some lifetime
249+
in scope. In this case, `'x` will be used wherever `'a` appears in the associated
250+
type definitions on impls.
224251

225252
```rust
226253
trait AssociatedType {
@@ -249,6 +276,37 @@ fn main() {
249276
}
250277
```
251278

279+
An example of associated types with generics and where clauses:
280+
281+
```rust
282+
struct ArrayLender<'a, T>(&'a mut [T; 16]);
283+
284+
trait Lend {
285+
// Generic associated type declaration
286+
type Lender<'a> where Self: 'a;
287+
fn lend<'a>(&'a mut self) -> Self::Lender<'a>;
288+
}
289+
290+
impl<T> Lend for [T; 16] {
291+
// Generic associated type definition
292+
type Lender<'a> = ArrayLender<'a, T> where Self: 'a;
293+
294+
fn lend<'a>(&'a mut self) -> Self::Lender<'a> {
295+
ArrayLender(self)
296+
}
297+
}
298+
299+
fn borrow<'a, T: Lend>(array: &'a mut T) -> <T as Lend>::Lender<'a> {
300+
array.lend()
301+
}
302+
303+
304+
fn main() {
305+
let mut array = [0usize; 16];
306+
let lender = borrow(&mut array);
307+
}
308+
```
309+
252310
### Associated Types Container Example
253311

254312
Consider the following example of a `Container` trait. Notice that the type is
@@ -279,6 +337,83 @@ impl<T> Container for Vec<T> {
279337
}
280338
```
281339

340+
### Relationship between `Bounds` and `WhereBounds`
341+
342+
In this example:
343+
344+
```rust
345+
# use std::fmt::Debug;
346+
trait Example {
347+
type Output<T>: Ord where T: Debug;
348+
}
349+
```
350+
351+
Given a reference to the associated type like `<X as Example>::Output<Y>`, the associated type itself must be `Ord`, and the type `Y` must be `Debug`.
352+
353+
### Required where clauses on generic associated types
354+
355+
Generic associated type declarations on traits currently may require a list of
356+
where clauses, dependent on functions in the trait and how the GAT is used. These
357+
rules may be loosened in the future; updates can be found [on the generic
358+
associated types initiative repository](https://rust-lang.github.io/generic-associated-types-initiative/explainer/required_bounds.html).
359+
360+
In a few words, these where clauses are required in order to maximize the allowed
361+
definitions of the associated type in impls. To do this, any clauses that *can be
362+
proven to hold* on functions (using the parameters of the function or trait)
363+
where a GAT appears as an input or output must also be written on the GAT itself.
364+
365+
```rust
366+
trait LendingIterator {
367+
type Item<'x> where Self: 'x;
368+
fn next<'a>(&'a mut self) -> Self::Item<'a>;
369+
}
370+
```
371+
372+
In the above, on the `next` function, we can prove that `Self: 'a`, because of
373+
the implied bounds from `&'a mut self`; therefore, we must write the equivalent
374+
bound on the GAT itself: `where Self: 'x`.
375+
376+
When there are multiple functions in a trait that use the GAT, then the
377+
*intersection* of the bounds from the different functions are used, rather than
378+
the union.
379+
380+
```rust
381+
trait Check<T> {
382+
type Checker<'x>;
383+
fn create_checker<'a>(item: &'a T) -> Self::Checker<'a>;
384+
fn do_check(checker: Self::Checker<'_>);
385+
}
386+
```
387+
388+
In this example, no bounds are required on the `type Checker<'a>;`. While we
389+
know that `T: 'a` on `create_checker`, we do not know that on `do_check`. However,
390+
if `do_check` was commented out, then the `where T: 'x` bound would be required
391+
on `Checker`.
392+
393+
The bounds on associated types also propagate required where clauses.
394+
395+
```rust
396+
trait Iterable {
397+
type Item<'a> where Self: 'a;
398+
type Iterator<'a>: Iterator<Item = Self::Item<'a>> where Self: 'a;
399+
fn iter<'a>(&'a self) -> Self::Iterator<'a>;
400+
}
401+
```
402+
403+
Here, `where Self: 'a` is required on `Item` because of `iter`. However, `Item`
404+
is used in the bounds of `Iterator`, the `where Self: 'a` clause is also required
405+
there.
406+
407+
Finally, any explicit uses of `'static` on GATs in the trait do not count towards
408+
the required bounds.
409+
410+
```rust
411+
trait StaticReturn {
412+
type Y<'a>;
413+
fn foo(&self) -> Self::Y<'static>;
414+
}
415+
```
416+
282417
## Associated Constants
283418

284419
*Associated constants* are [constants] associated with a type.

src/items/traits.md

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ Object safe traits can be the base trait of a [trait object]. A trait is
7070
* All [supertraits] must also be object safe.
7171
* `Sized` must not be a [supertrait][supertraits]. In other words, it must not require `Self: Sized`.
7272
* It must not have any associated constants.
73+
* It must not have any associated types with generics.
7374
* All associated functions must either be dispatchable from a trait object or be explicitly non-dispatchable:
7475
* Dispatchable functions require:
7576
* Not have any type parameters (although lifetime parameters are allowed),

src/items/type-aliases.md

+13-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
> _TypeAlias_ :\
55
> &nbsp;&nbsp; `type` [IDENTIFIER]&nbsp;[_GenericParams_]<sup>?</sup>
66
> ( `:` [_TypeParamBounds_] )<sup>?</sup>
7-
> [_WhereClause_]<sup>?</sup> ( `=` [_Type_] )<sup>?</sup> `;`
7+
> [_WhereClause_]<sup>?</sup> ( `=` [_Type_] [_WhereClause_]<sup>?</sup>)<sup>?</sup> `;`
88
99
A _type alias_ defines a new name for an existing [type]. Type aliases are
1010
declared with the keyword `type`. Every value has a single, specific type, but
@@ -31,11 +31,18 @@ let _ = UseAlias(5); // OK
3131
let _ = TypeAlias(5); // Doesn't work
3232
```
3333

34-
A type alias without the [_Type_] specification may only appear as an
35-
[associated type] in a [trait].
34+
A type alias, when not used as an associated type, must include a [_Type_] and
35+
may not include [_TypeParamBounds_].
3636

37-
A type alias with [_TypeParamBounds_] may only specified when used as
38-
an [associated type] in a [trait].
37+
A type alias, when used as an [associated type] in a [trait], must not include a
38+
[_Type_] specification but may include [_TypeParamBounds_].
39+
40+
A type alias, when used as an [associated type] in a [trait impl], must include
41+
a [_Type_] specification and may not include [_TypeParamBounds_].
42+
43+
Where clauses before the equals sign on a type alias in a [trait impl] (like
44+
`type TypeAlias<T> where T: Foo = Bar<T>`) are deprecated. Where clauses after
45+
the equals sign (like `type TypeAlias<T> = Bar<T> where T: Foo`) are preferred.
3946

4047
[IDENTIFIER]: ../identifiers.md
4148
[_GenericParams_]: generics.md
@@ -45,3 +52,4 @@ an [associated type] in a [trait].
4552
[associated type]: associated-items.md#associated-types
4653
[trait]: traits.md
4754
[type]: ../types.md
55+
[trait impl]: implementations.md#trait-implementations

0 commit comments

Comments
 (0)