]> git.lizzy.rs Git - rust.git/blob - src/doc/trpl/generics.md
rollup merge of #21151: brson/beta
[rust.git] / src / doc / trpl / generics.md
1 % Generics
2
3 Sometimes, when writing a function or data type, we may want it to work for
4 multiple types of arguments. For example, remember our `OptionalInt` type?
5
6 ```{rust}
7 enum OptionalInt {
8     Value(int),
9     Missing,
10 }
11 ```
12
13 If we wanted to also have an `OptionalFloat64`, we would need a new enum:
14
15 ```{rust}
16 enum OptionalFloat64 {
17     Valuef64(f64),
18     Missingf64,
19 }
20 ```
21
22 This is really unfortunate. Luckily, Rust has a feature that gives us a better
23 way: generics. Generics are called *parametric polymorphism* in type theory,
24 which means that they are types or functions that have multiple forms (*poly*
25 is multiple, *morph* is form) over a given parameter (*parametric*).
26
27 Anyway, enough with type theory declarations, let's check out the generic form
28 of `OptionalInt`. It is actually provided by Rust itself, and looks like this:
29
30 ```rust
31 enum Option<T> {
32     Some(T),
33     None,
34 }
35 ```
36
37 The `<T>` part, which you've seen a few times before, indicates that this is
38 a generic data type. Inside the declaration of our enum, wherever we see a `T`,
39 we substitute that type for the same type used in the generic. Here's an
40 example of using `Option<T>`, with some extra type annotations:
41
42 ```{rust}
43 let x: Option<int> = Some(5i);
44 ```
45
46 In the type declaration, we say `Option<int>`. Note how similar this looks to
47 `Option<T>`. So, in this particular `Option`, `T` has the value of `int`. On
48 the right-hand side of the binding, we do make a `Some(T)`, where `T` is `5i`.
49 Since that's an `int`, the two sides match, and Rust is happy. If they didn't
50 match, we'd get an error:
51
52 ```{rust,ignore}
53 let x: Option<f64> = Some(5i);
54 // error: mismatched types: expected `core::option::Option<f64>`
55 // but found `core::option::Option<int>` (expected f64 but found int)
56 ```
57
58 That doesn't mean we can't make `Option<T>`s that hold an `f64`! They just have to
59 match up:
60
61 ```{rust}
62 let x: Option<int> = Some(5i);
63 let y: Option<f64> = Some(5.0f64);
64 ```
65
66 This is just fine. One definition, multiple uses.
67
68 Generics don't have to only be generic over one type. Consider Rust's built-in
69 `Result<T, E>` type:
70
71 ```{rust}
72 enum Result<T, E> {
73     Ok(T),
74     Err(E),
75 }
76 ```
77
78 This type is generic over _two_ types: `T` and `E`. By the way, the capital letters
79 can be any letter you'd like. We could define `Result<T, E>` as:
80
81 ```{rust}
82 enum Result<H, N> {
83     Ok(H),
84     Err(N),
85 }
86 ```
87
88 if we wanted to. Convention says that the first generic parameter should be
89 `T`, for 'type,' and that we use `E` for 'error.' Rust doesn't care, however.
90
91 The `Result<T, E>` type is intended to be used to return the result of a
92 computation, and to have the ability to return an error if it didn't work out.
93 Here's an example:
94
95 ```{rust}
96 let x: Result<f64, String> = Ok(2.3f64);
97 let y: Result<f64, String> = Err("There was an error.".to_string());
98 ```
99
100 This particular Result will return an `f64` if there's a success, and a
101 `String` if there's a failure. Let's write a function that uses `Result<T, E>`:
102
103 ```{rust}
104 fn inverse(x: f64) -> Result<f64, String> {
105     if x == 0.0f64 { return Err("x cannot be zero!".to_string()); }
106
107     Ok(1.0f64 / x)
108 }
109 ```
110
111 We don't want to take the inverse of zero, so we check to make sure that we
112 weren't passed zero. If we were, then we return an `Err`, with a message. If
113 it's okay, we return an `Ok`, with the answer.
114
115 Why does this matter? Well, remember how `match` does exhaustive matches?
116 Here's how this function gets used:
117
118 ```{rust}
119 # fn inverse(x: f64) -> Result<f64, String> {
120 #     if x == 0.0f64 { return Err("x cannot be zero!".to_string()); }
121 #     Ok(1.0f64 / x)
122 # }
123 let x = inverse(25.0f64);
124
125 match x {
126     Ok(x) => println!("The inverse of 25 is {}", x),
127     Err(msg) => println!("Error: {}", msg),
128 }
129 ```
130
131 The `match` enforces that we handle the `Err` case. In addition, because the
132 answer is wrapped up in an `Ok`, we can't just use the result without doing
133 the match:
134
135 ```{rust,ignore}
136 let x = inverse(25.0f64);
137 println!("{}", x + 2.0f64); // error: binary operation `+` cannot be applied
138            // to type `core::result::Result<f64,collections::string::String>`
139 ```
140
141 This function is great, but there's one other problem: it only works for 64 bit
142 floating point values. What if we wanted to handle 32 bit floating point as
143 well? We'd have to write this:
144
145 ```{rust}
146 fn inverse32(x: f32) -> Result<f32, String> {
147     if x == 0.0f32 { return Err("x cannot be zero!".to_string()); }
148
149     Ok(1.0f32 / x)
150 }
151 ```
152
153 Bummer. What we need is a *generic function*. Luckily, we can write one!
154 However, it won't _quite_ work yet. Before we get into that, let's talk syntax.
155 A generic version of `inverse` would look something like this:
156
157 ```{rust,ignore}
158 fn inverse<T>(x: T) -> Result<T, String> {
159     if x == 0.0 { return Err("x cannot be zero!".to_string()); }
160
161     Ok(1.0 / x)
162 }
163 ```
164
165 Just like how we had `Option<T>`, we use a similar syntax for `inverse<T>`.
166 We can then use `T` inside the rest of the signature: `x` has type `T`, and half
167 of the `Result` has type `T`. However, if we try to compile that example, we'll get
168 an error:
169
170 ```text
171 error: binary operation `==` cannot be applied to type `T`
172 ```
173
174 Because `T` can be _any_ type, it may be a type that doesn't implement `==`,
175 and therefore, the first line would be wrong. What do we do?
176
177 To fix this example, we need to learn about another Rust feature: traits.