Associated Types
In Rust's type system, an associated type (type Output;) is a type-level function attached to a trait implementation that maps a concrete implementing type Self to a uniquely determined companion type Self::Output.
Associated Types as Type-Level Projections (<T as Trait>::Assoc)
When a type T implements a trait Trait { type Output; }, the type system treats <T as Trait>::Output (or T::Output when unambiguous) as a type projection—computing a concrete type from T:
Input Type (`Self`) Trait Implementation Projected Type (`Self::Item`)
-------------------- -------------------- -----------------------------
std::vec::IntoIter<String> ──(impl Iterator)────────────> String
std::slice::Iter<'a, u32> ──(impl Iterator)────────────> &'a u32
std::str::Chars<'a> ──(impl Iterator)────────────> char
Constraining Associated Types in Generic Signatures
A generic function can constrain an associated type in three ways:
- Equality constraint (
I: Iterator<Item = u32>): Fixes the associated type to a specific concrete type. - Trait bound on the projected type (
where I: Iterator, I::Item: Debug): Allows anyItemtype as long as it implementsDebug. - Associated type bound syntax (
I: Iterator<Item: Debug>): Concise inline syntax (stabilized in Rust 1.79) equivalent towhere I: Iterator, I::Item: Debug!
use std::fmt::Debug;
// Inline associated type bound `I: IntoIterator<Item: Debug>`:
fn print_all<I>(iterable: I)
where
I: IntoIterator<Item: Debug>,
{
for item in iterable {
print!("{item:?} ");
}
println!();
}
fn main() {
print_all([10, 20, 30]);
print_all(["rust", "2024"]);
}
10 20 30
"rust" "2024"
Generic Associated Types (GATs)
An associated type inside a trait can also declare its own generic lifetime or type parameters (type Item<'a> where Self: 'a;), known as a Generic Associated Type (GAT). GATs allow a trait to project a type that borrows from &'a self (such as a streaming iterator or zero-copy cursor whose yielded item borrows internal buffer memory) or construct a family of containers (type Container<T>;):
trait WindowSource {
// Generic Associated Type parameterized over borrow lifetime `'a`:
type Window<'a>
where
Self: 'a;
fn get_window(&self, start: usize, len: usize) -> Option<Self::Window<'_>>;
}
impl<T> WindowSource for Vec<T> {
type Window<'a>
= &'a [T]
where
T: 'a;
fn get_window(&self, start: usize, len: usize) -> Option<Self::Window<'_>> {
self.get(start..start + len)
}
}
fn main() {
let data = vec![10, 20, 30, 40, 50];
println!("Window 1..4: {:?}", data.get_window(1, 3));
}
Window 1..4: Some([20, 30, 40])