3 Associated types are a powerful part of Rust’s type system. They’re related to
4 the idea of a ‘type family’, in other words, grouping multiple types together. That
5 description is a bit abstract, so let’s dive right into an example. If you want
6 to write a `Graph` trait, you have two types to be generic over: the node type
7 and the edge type. So you might write a trait, `Graph<N, E>`, that looks like
12 fn has_edge(&self, &N, &N) -> bool;
13 fn edges(&self, &N) -> Vec<E>;
18 While this sort of works, it ends up being awkward. For example, any function
19 that wants to take a `Graph` as a parameter now _also_ needs to be generic over
20 the `N`ode and `E`dge types too:
23 fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 { ... }
26 Our distance calculation works regardless of our `Edge` type, so the `E` stuff in
27 this signature is a distraction.
29 What we really want to say is that a certain `E`dge and `N`ode type come together
30 to form each kind of `Graph`. We can do that with associated types:
37 fn has_edge(&self, &Self::N, &Self::N) -> bool;
38 fn edges(&self, &Self::N) -> Vec<Self::E>;
43 Now, our clients can be abstract over a given `Graph`:
46 fn distance<G: Graph>(graph: &G, start: &G::N, end: &G::N) -> u32 { ... }
49 No need to deal with the `E`dge type here!
51 Let’s go over all this in more detail.
53 ## Defining associated types
55 Let’s build that `Graph` trait. Here’s the definition:
62 fn has_edge(&self, &Self::N, &Self::N) -> bool;
63 fn edges(&self, &Self::N) -> Vec<Self::E>;
67 Simple enough. Associated types use the `type` keyword, and go inside the body
68 of the trait, with the functions.
70 These type declarations work the same way as those for functions. For example,
71 if we wanted our `N` type to implement `Display`, so we can print the nodes out,
81 fn has_edge(&self, &Self::N, &Self::N) -> bool;
82 fn edges(&self, &Self::N) -> Vec<Self::E>;
86 ## Implementing associated types
88 Just like any trait, traits that use associated types use the `impl` keyword to
89 provide implementations. Here’s a simple implementation of Graph:
95 # fn has_edge(&self, &Self::N, &Self::N) -> bool;
96 # fn edges(&self, &Self::N) -> Vec<Self::E>;
104 impl Graph for MyGraph {
108 fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
112 fn edges(&self, n: &Node) -> Vec<Edge> {
118 This silly implementation always returns `true` and an empty `Vec<Edge>`, but it
119 gives you an idea of how to implement this kind of thing. We first need three
120 `struct`s, one for the graph, one for the node, and one for the edge. If it made
121 more sense to use a different type, that would work as well, we’re going to
122 use `struct`s for all three here.
124 Next is the `impl` line, which is an implementation like any other trait.
126 From here, we use `=` to define our associated types. The name the trait uses
127 goes on the left of the `=`, and the concrete type we’re `impl`ementing this
128 for goes on the right. Finally, we use the concrete types in our function
131 ## Trait objects with associated types
133 There’s one more bit of syntax we should talk about: trait objects. If you
134 try to create a trait object from a trait with an associated type, like this:
140 # fn has_edge(&self, &Self::N, &Self::N) -> bool;
141 # fn edges(&self, &Self::N) -> Vec<Self::E>;
146 # impl Graph for MyGraph {
149 # fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
152 # fn edges(&self, n: &Node) -> Vec<Edge> {
157 let obj = Box::new(graph) as Box<Graph>;
160 You’ll get two errors:
163 error: the value of the associated type `E` (from the trait `main::Graph`) must
165 let obj = Box::new(graph) as Box<Graph>;
166 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
167 24:44 error: the value of the associated type `N` (from the trait
168 `main::Graph`) must be specified [E0191]
169 let obj = Box::new(graph) as Box<Graph>;
170 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173 We can’t create a trait object like this, because we don’t know the associated
174 types. Instead, we can write this:
180 # fn has_edge(&self, &Self::N, &Self::N) -> bool;
181 # fn edges(&self, &Self::N) -> Vec<Self::E>;
186 # impl Graph for MyGraph {
189 # fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
192 # fn edges(&self, n: &Node) -> Vec<Edge> {
197 let obj = Box::new(graph) as Box<Graph<N=Node, E=Edge>>;
200 The `N=Node` syntax allows us to provide a concrete type, `Node`, for the `N`
201 type parameter. Same with `E=Edge`. If we didn’t provide this constraint, we
202 couldn’t be sure which `impl` to match this trait object to.