---
title: Associated Types and GATs
url: https://doc.liz6.com/en/rust/03-advanced-generics-and-traits/03-associated-types-and-gats
locale: en
area: rust
tags:
- rust
- advanced-generics-and-traits
date: 2026-06-30
modified: 2026-07-16
description: Associated types (type Item) allow each implementation of a trait to have only one Item type, complementing generic parameters (where each implementation can have infinite Ts)—associated types enforce uniqueness, while generic parameters allow diversity. GATs (Generic Associated Types, 1.65+) allow associated types themselves to carry generic parameters, unlocking abstractions that were previously impossible to express, such as traits that return borrowed data from themselves and generic iterators...
---

# Associated Types and GATs

> Associated types (`type Item`) allow each implementation of a trait to have only one `Item` type, complementing generic parameters (where each implementation can have infinite `T`s)—associated types enforce uniqueness, while generic parameters allow diversity. GATs (Generic Associated Types, 1.65+) allow associated types themselves to carry generic parameters, unlocking abstractions that were previously impossible to express, such as traits that return borrowed data from themselves and generic iterators.

## Associated Types: One Concrete Type per `impl`

```rust
trait Iterator {
    type Item;                       // Associated type: each implementer specifies a concrete type
    fn next(&mut self) -> Option<Self::Item>;
}

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<u32> { ... }
}
```

### Why Not Use Generic Parameters?

You certainly could write `trait Iterator<T> { fn next(&mut self) -> Option<T>; }`. However, this would mean that `Counter` could simultaneously implement `Iterator<i32>`, `Iterator<String>`, and `Iterator<f64>`—one independent `impl` for each `T`. This doesn't make sense for `Iterator`—a type should only produce one kind of item.

Associated types express a "one-to-one" semantic constraint: **at most one `impl` per type**. Generic parameters express a "one-to-many" relationship: a type can have **multiple `impl`s** for different generic parameters.

This is not just a matter of semantic preference—it affects type inference. When using associated types, `Iterator::Item` can be uniquely determined from `Self`. When using generic parameters, the compiler must infer `T` from the context. Associated types make the compiler's job simpler and the API clearer.

## GATs (Generic Associated Types): Associated Types with Lifetimes

Rust 1.65 introduced GATs—associated types can now have their own lifetime or generic parameters:

```rust
trait LendingIterator {
    type Item<'a> where Self: 'a;    // Associated type with a lifetime parameter!
    fn next(&mut self) -> Option<Self::Item<'_>>;
}
```

What problem do GATs solve? The traditional `Iterator` trait cannot express "returning a reference that borrows from `self`." You cannot write `type Item = &[u8]` because references must have a lifetime—and there was nowhere to write a lifetime on an associated type. GATs' `type Item<'a>` allows associated types to use lifetimes, making `Item<'a> = &'a [u8]` possible—the returned reference is tied to the lifetime of `&self`.

This is known as a **lending iterator**—the iterator "lends" data rather than transferring ownership. The standard library's `Iterator` is not lending (`next` returns `Option<Self::Item>`, it does not borrow `&mut self`), so you cannot hold a reference to the iterator's internals on the elements returned by the iterator. GATs remove this limitation.

GATs are also the foundation for async traits. The `Future` type generated by `async fn` is anonymous and cannot be expressed directly in a trait. GATs allow forms like `type Future<'a>: Future<Output = X>`—a trait can have a method that returns a `Future` without the heap allocation overhead of `Box<dyn Future>`.

## `impl Trait` in Return Position

```rust
fn make_iter() -> impl Iterator<Item = i32> {
    (0..10).filter(|x| x % 2 == 0)    // Returns an anonymous type
}
```

The caller does not know the concrete return type—they only know that "it implements `Iterator<Item = i32>`." The compiler knows, so it can perform static dispatch and inlining. The difference from `Box<dyn Iterator>`: `impl Trait` uses static dispatch (zero overhead, no virtual function calls), but the caller cannot assign functions with different return types to each other—this is the cost of the compiler's abstraction boundary.

## References

- **RFC 1598**: Generic Associated Types
- **Rust Blog**: "GATs are stable!" (2022)
- **Rust Book**: Chapter 19.4

*Keywords: associated type, GAT, lending iterator, impl Trait, static dispatch, type inference*
