]> git.proxmox.com Git - rustc.git/blame - src/doc/trpl/associated-types.md
Imported Upstream version 1.2.0+dfsg1
[rustc.git] / src / doc / trpl / associated-types.md
CommitLineData
c34b1796
AL
1% Associated Types
2
bd371182
AL
3Associated types are a powerful part of Rust’s type system. They’re related to
4the idea of a ‘type family’, in other words, grouping multiple types together. That
5description is a bit abstract, so let’s dive right into an example. If you want
c34b1796
AL
6to write a `Graph` trait, you have two types to be generic over: the node type
7and the edge type. So you might write a trait, `Graph<N, E>`, that looks like
8this:
9
10```rust
11trait Graph<N, E> {
12 fn has_edge(&self, &N, &N) -> bool;
13 fn edges(&self, &N) -> Vec<E>;
14 // etc
15}
16```
17
18While this sort of works, it ends up being awkward. For example, any function
19that wants to take a `Graph` as a parameter now _also_ needs to be generic over
20the `N`ode and `E`dge types too:
21
22```rust,ignore
23fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 { ... }
24```
25
26Our distance calculation works regardless of our `Edge` type, so the `E` stuff in
27this signature is just a distraction.
28
29What we really want to say is that a certain `E`dge and `N`ode type come together
30to form each kind of `Graph`. We can do that with associated types:
31
32```rust
33trait 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
43Now, our clients can be abstract over a given `Graph`:
44
45```rust,ignore
62682a34 46fn distance<G: Graph>(graph: &G, start: &G::N, end: &G::N) -> u32 { ... }
c34b1796
AL
47```
48
49No need to deal with the `E`dge type here!
50
bd371182 51Let’s go over all this in more detail.
c34b1796
AL
52
53## Defining associated types
54
bd371182 55Let’s build that `Graph` trait. Here’s the definition:
c34b1796
AL
56
57```rust
58trait 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
67Simple enough. Associated types use the `type` keyword, and go inside the body
68of the trait, with the functions.
69
70These `type` declarations can have all the same thing as functions do. For example,
71if we wanted our `N` type to implement `Display`, so we can print the nodes out,
72we could do this:
73
74```rust
75use std::fmt;
76
77trait 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
88Just like any trait, traits that use associated types use the `impl` keyword to
bd371182 89provide implementations. Here’s a simple implementation of Graph:
c34b1796
AL
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# }
98struct Node;
99
100struct Edge;
101
102struct MyGraph;
103
104impl 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
118This silly implementation always returns `true` and an empty `Vec<Edge>`, but it
119gives 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
bd371182 121more sense to use a different type, that would work as well, we’re just going to
c34b1796
AL
122use `struct`s for all three here.
123
124Next is the `impl` line, which is just like implementing any other trait.
125
126From here, we use `=` to define our associated types. The name the trait uses
bd371182 127goes on the left of the `=`, and the concrete type we’re `impl`ementing this
c34b1796
AL
128for goes on the right. Finally, we use the concrete types in our function
129declarations.
130
131## Trait objects with associated types
132
133There’s one more bit of syntax we should talk about: trait objects. If you
134try to create a trait object from 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# }
156let graph = MyGraph;
157let obj = Box::new(graph) as Box<Graph>;
158```
159
160You’ll get two errors:
161
162```text
163error: the value of the associated type `E` (from the trait `main::Graph`) must
164be specified [E0191]
165let obj = Box::new(graph) as Box<Graph>;
166 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16724:44 error: the value of the associated type `N` (from the trait
168`main::Graph`) must be specified [E0191]
169let obj = Box::new(graph) as Box<Graph>;
170 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
171```
172
173We can’t create a trait object like this, because we don’t know the associated
174types. 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# }
196let graph = MyGraph;
197let obj = Box::new(graph) as Box<Graph<N=Node, E=Edge>>;
198```
199
200The `N=Node` syntax allows us to provide a concrete type, `Node`, for the `N`
9346a6ac 201type parameter. Same with `E=Edge`. If we didn’t provide this constraint, we
c34b1796 202couldn’t be sure which `impl` to match this trait object to.