Associated Type Projections
An associated type projection is a type-level expression—written T::Assoc (shorthand) or <T as Trait>::Assoc (fully qualified syntax)—that resolves an associated type defined on a Trait for a concrete or generic type T. In type-level programming, associated type projections act as type-level functions: given an input type T, the compiler computes the output type <T as Trait>::Assoc during type checking (normalization).
Type-Level Function Analogy:
Value-Level Function: fn output_of(x: usize) -> usize
Type-Level Projection: <InputType as ComputeTrait>::OutputType
Shorthand vs. Fully Qualified Projection Syntax
| Syntax | Where It Works | Example |
|---|---|---|
Self::Assoc |
Inside the trait or impl Trait for Type block |
fn next(&mut self) -> Option<Self::Item> |
T::Assoc |
Generic context where T has a single unambiguous bound T: Trait |
fn first<I: Iterator>(mut i: I) -> Option<I::Item> |
<T as Trait>::Assoc |
Concrete types (<Vec<u8> as IntoIterator>::IntoIter), chained projections, or disambiguating two traits with the same associated type name |
<Vec<i32> as IntoIterator>::Item |
Why Concrete Types Require <Type as Trait>::Assoc (E0223)
Writing Vec::<i32>::Item or String::Err directly on a concrete type is rejected with compiler error E0223 (ambiguous associated type), because multiple traits in scope or downstream crates could define an associated type named Item or Err. Qualification via <Type as Trait>::Assoc makes the projection unambiguous:
// ❌ does not compile
fn main() {
let x: Vec<i32>::Item = 10;
}
error[E0223]: ambiguous associated type
--> src/main.rs:3:12
|
3 | let x: Vec<i32>::Item = 10;
| ^^^^^^^^^^^^^^
|
help: if there were a trait named `Example` with associated type `Item` implemented for `Vec<i32>`, you could use the fully-qualified path
|
3 - let x: Vec<i32>::Item = 10;
3 + let x: <Vec<i32> as Example>::Item = 10;
Chained Associated Type Projections
Projections can be nested arbitrarily to traverse multi-step type relationships—for example, extracting the item type of an IntoIterator's IntoIter or chaining request/response handler traits:
trait Service {
type Request;
type Response;
fn call(&self, req: Self::Request) -> Self::Response;
}
trait Codec {
type Decoded: Service;
}
// Chained projection: `<C::Decoded as Service>::Response`
fn dispatch_decoded<C: Codec>(
service: &C::Decoded,
req: <C::Decoded as Service>::Request,
) -> <C::Decoded as Service>::Response {
service.call(req)
}
struct Doubler;
impl Service for Doubler {
type Request = i32;
type Response = i64;
fn call(&self, req: i32) -> i64 {
i64::from(req) * 2
}
}
struct BinaryCodec;
impl Codec for BinaryCodec {
type Decoded = Doubler;
}
fn main() {
let res = dispatch_decoded::<BinaryCodec>(&Doubler, 21);
println!("dispatched response: {}", res);
}
dispatched response: 42