Rust Associated Types
An associated type is a type named inside a trait. Each implementation fills in that name once.
The trait leaves a blank
Inside the trait, type Item; is a named blank — the trait says the blank exists, not what fills it:
trait Words {
type Item;
fn next_word(&mut self) -> Option<Self::Item>;
}Each impl fills it in once
The implementation names the type one time — from then on, Self::Item means String for this type:
impl Words for OneWord {
type Item = String;
}Name the associated type from outside
Code outside the trait names the filled-in type through the implementor: I::Item. head works for every iterator and answers in that iterator's own item type:
fn head<I: Iterator>(mut it: I) -> Option<I::Item> {
it.next()
}
println!("head = {:?}", head(vec![9, 8, 7].into_iter()));No default yet in stable Rust
The trait may not pre-fill its own blank — associated type defaults are still experimental, so this is refused:
trait Words {
type Item = i32; // error E0658: associated type defaults are unstable
}Example
use std::ops::Add;
#[derive(Debug, PartialEq)]
struct Point { x: i32, y: i32 }
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point { Point { x: self.x + other.x, y: self.y + other.y } }
}
fn main() { println!("{:?}", Point { x: 1, y: 2 } + Point { x: 3, y: 4 }); }Key points
- Use an associated type when there is exactly one sensible choice per implementation
- Use a generic parameter on the trait instead.
- Do it when one type may implement it in several ways, like From<T> for many source types
- Iterator's Item is an associated type.
- Add's Output is an associated type.
- An associated type names one type inside a trait for each implementation to fill in.
In this example, Point gets the + operator by implementing the Add trait. Add requires an associated type, Output: the type that Point + Point produces. The impl sets type Output = Point and writes add to combine the x values and the y values. So (1, 2) + (3, 4) gives Point { x: 4, y: 6 }. The {:?} in the println! shows the new point. The #[derive] lines give Point its Debug printing and equality tests for free.
Note
An associated type keeps signatures short.
You write Self::Item instead of carrying an extra generic letter through every function.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Why does Iterator use an associated type Item instead of a generic parameter?