]> git.proxmox.com Git - rustc.git/blob - src/doc/book/src/associated-types.md
New upstream version 1.17.0+dfsg1
[rustc.git] / src / doc / book / src / associated-types.md
1 # Associated Types
2
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
8 this:
9
10 ```rust
11 trait Graph<N, E> {
12 fn has_edge(&self, &N, &N) -> bool;
13 fn edges(&self, &N) -> Vec<E>;
14 // Etc.
15 }
16 ```
17
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:
21
22 ```rust,ignore
23 fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 { ... }
24 ```
25
26 Our distance calculation works regardless of our `Edge` type, so the `E` stuff in
27 this signature is a distraction.
28
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:
31
32 ```rust
33 trait Graph {
34 type N;
35 type E;
36
37 fn has_edge(&self, &Self::N, &Self::N) -> bool;
38 fn edges(&self, &Self::N) -> Vec<Self::E>;
39 // Etc.
40 }
41 ```
42
43 Now, our clients can be abstract over a given `Graph`:
44
45 ```rust,ignore
46 fn distance<G: Graph>(graph: &G, start: &G::N, end: &G::N) -> u32 { ... }
47 ```
48
49 No need to deal with the `E`dge type here!
50
51 Let’s go over all this in more detail.
52
53 ## Defining associated types
54
55 Let’s build that `Graph` trait. Here’s the definition:
56
57 ```rust
58 trait Graph {
59 type N;
60 type E;
61
62 fn has_edge(&self, &Self::N, &Self::N) -> bool;
63 fn edges(&self, &Self::N) -> Vec<Self::E>;
64 }
65 ```
66
67 Simple enough. Associated types use the `type` keyword, and go inside the body
68 of the trait, with the functions.
69
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,
72 we could do this:
73
74 ```rust
75 use std::fmt;
76
77 trait Graph {
78 type N: fmt::Display;
79 type E;
80
81 fn has_edge(&self, &Self::N, &Self::N) -> bool;
82 fn edges(&self, &Self::N) -> Vec<Self::E>;
83 }
84 ```
85
86 ## Implementing associated types
87
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:
90
91 ```rust
92 # trait Graph {
93 # type N;
94 # type E;
95 # fn has_edge(&self, &Self::N, &Self::N) -> bool;
96 # fn edges(&self, &Self::N) -> Vec<Self::E>;
97 # }
98 struct Node;
99
100 struct Edge;
101
102 struct MyGraph;
103
104 impl Graph for MyGraph {
105 type N = Node;
106 type E = Edge;
107
108 fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
109 true
110 }
111
112 fn edges(&self, n: &Node) -> Vec<Edge> {
113 Vec::new()
114 }
115 }
116 ```
117
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.
123
124 Next is the `impl` line, which is an implementation like any other trait.
125
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
129 declarations.
130
131 ## Trait objects with associated types
132
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:
135
136 ```rust,ignore
137 # trait Graph {
138 # type N;
139 # type E;
140 # fn has_edge(&self, &Self::N, &Self::N) -> bool;
141 # fn edges(&self, &Self::N) -> Vec<Self::E>;
142 # }
143 # struct Node;
144 # struct Edge;
145 # struct MyGraph;
146 # impl Graph for MyGraph {
147 # type N = Node;
148 # type E = Edge;
149 # fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
150 # true
151 # }
152 # fn edges(&self, n: &Node) -> Vec<Edge> {
153 # Vec::new()
154 # }
155 # }
156 let graph = MyGraph;
157 let obj = Box::new(graph) as Box<Graph>;
158 ```
159
160 You’ll get two errors:
161
162 ```text
163 error: the value of the associated type `E` (from the trait `main::Graph`) must
164 be specified [E0191]
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 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
171 ```
172
173 We can’t create a trait object like this, because we don’t know the associated
174 types. Instead, we can write this:
175
176 ```rust
177 # trait Graph {
178 # type N;
179 # type E;
180 # fn has_edge(&self, &Self::N, &Self::N) -> bool;
181 # fn edges(&self, &Self::N) -> Vec<Self::E>;
182 # }
183 # struct Node;
184 # struct Edge;
185 # struct MyGraph;
186 # impl Graph for MyGraph {
187 # type N = Node;
188 # type E = Edge;
189 # fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
190 # true
191 # }
192 # fn edges(&self, n: &Node) -> Vec<Edge> {
193 # Vec::new()
194 # }
195 # }
196 let graph = MyGraph;
197 let obj = Box::new(graph) as Box<Graph<N=Node, E=Edge>>;
198 ```
199
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.