]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch10-01-syntax.md
New upstream version 1.26.0+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch10-01-syntax.md
CommitLineData
cc61c64b
XL
1## Generic Data Types
2
0531ce1d
XL
3We can use generics to create definitions for items like function signatures or
4structs, which we can then use with many different concrete data types. Let’s
5first look at how to define functions, structs, enums, and methods using
6generics. Then we’ll discuss how generics affect code performance.
cc61c64b 7
0531ce1d 8### In Function Definitions
cc61c64b 9
0531ce1d
XL
10When defining a function that uses generics, we place the generics in the
11signature of the function where we would usually specify the data types of the
12parameters and return value. Doing so makes our code more flexible and provides
13more functionality to callers of our function while preventing code duplication.
cc61c64b 14
0531ce1d
XL
15Continuing with our `largest` function, Listing 10-4 shows two functions that
16both find the largest value in a slice:
cc61c64b
XL
17
18<span class="filename">Filename: src/main.rs</span>
19
20```rust
21fn largest_i32(list: &[i32]) -> i32 {
22 let mut largest = list[0];
23
24 for &item in list.iter() {
25 if item > largest {
26 largest = item;
27 }
28 }
29
30 largest
31}
32
33fn largest_char(list: &[char]) -> char {
34 let mut largest = list[0];
35
36 for &item in list.iter() {
37 if item > largest {
38 largest = item;
39 }
40 }
41
42 largest
43}
44
45fn main() {
3b2f2976 46 let number_list = vec![34, 50, 25, 100, 65];
cc61c64b 47
3b2f2976 48 let result = largest_i32(&number_list);
cc61c64b
XL
49 println!("The largest number is {}", result);
50# assert_eq!(result, 100);
51
3b2f2976 52 let char_list = vec!['y', 'm', 'a', 'q'];
cc61c64b 53
3b2f2976 54 let result = largest_char(&char_list);
cc61c64b
XL
55 println!("The largest char is {}", result);
56# assert_eq!(result, 'y');
57}
58```
59
60<span class="caption">Listing 10-4: Two functions that differ only in their
61names and the types in their signatures</span>
62
0531ce1d
XL
63The `largest_i32` function is the one we extracted in Listing 10-3 that finds
64the largest `i32` in a slice. The `largest_char` function finds the largest
65`char` in a slice. The function bodies have the same code, so let’s eliminate
66the duplication by introducing a generic type parameter in a single function.
cc61c64b 67
0531ce1d
XL
68To parameterize the types in the new function we’ll define, we need to name the
69type parameter, just like we do for the value parameters to a function. You can
70use any identifier as a type parameter name. But we’ll use `T` because, by
71convention, parameter names in Rust are short, often just a letter, and Rust’s
72type naming convention is CamelCase. Short for “type,” `T` is the default
73choice of most Rust programmers.
cc61c64b
XL
74
75When we use a parameter in the body of the function, we have to declare the
0531ce1d
XL
76parameter name in the signature so that the compiler knows what that name
77means. Similarly, when we use a type parameter name in a function signature, we
78have to declare the type parameter name before we use it. To define the generic
79`largest` function, place type name declarations inside angle brackets (`<>`)
80between the name of the function and the parameter list, like this:
cc61c64b
XL
81
82```rust,ignore
83fn largest<T>(list: &[T]) -> T {
84```
85
0531ce1d
XL
86We read this definition as: the function `largest` is generic over some type
87`T`. This function has one parameter named `list`, which is a slice of values
88of type `T`. The `largest` function will return a value of the same type `T`.
cc61c64b 89
0531ce1d
XL
90Listing 10-5 shows the combined `largest` function definition using the generic
91data type in its signature. The listing also shows how we can call the function
92with either a slice of `i32` values or `char` values. Note that this code won’t
93compile yet, but we’ll fix it later in this chapter.
cc61c64b
XL
94
95<span class="filename">Filename: src/main.rs</span>
96
97```rust,ignore
98fn largest<T>(list: &[T]) -> T {
99 let mut largest = list[0];
100
101 for &item in list.iter() {
102 if item > largest {
103 largest = item;
104 }
105 }
106
107 largest
108}
109
110fn main() {
3b2f2976 111 let number_list = vec![34, 50, 25, 100, 65];
cc61c64b 112
3b2f2976 113 let result = largest(&number_list);
cc61c64b
XL
114 println!("The largest number is {}", result);
115
3b2f2976 116 let char_list = vec!['y', 'm', 'a', 'q'];
cc61c64b 117
3b2f2976 118 let result = largest(&char_list);
cc61c64b
XL
119 println!("The largest char is {}", result);
120}
121```
122
123<span class="caption">Listing 10-5: A definition of the `largest` function that
3b2f2976 124uses generic type parameters but doesn’t compile yet</span>
cc61c64b 125
0531ce1d 126If we compile this code right now, we’ll get this error:
cc61c64b
XL
127
128```text
129error[E0369]: binary operation `>` cannot be applied to type `T`
0531ce1d 130 --> src/main.rs:5:12
cc61c64b
XL
131 |
1325 | if item > largest {
0531ce1d 133 | ^^^^^^^^^^^^^^
cc61c64b 134 |
0531ce1d 135 = note: an implementation of `std::cmp::PartialOrd` might be missing for `T`
cc61c64b
XL
136```
137
0531ce1d
XL
138The note mentions `std::cmp::PartialOrd`, which is a *trait*. We’ll talk about
139traits in the next section. For now, this error states that the body of
140`largest` won’t work for all possible types that `T` could be. Because we want
141to compare values of type `T` in the body, we can only use types whose values
142can be ordered. To enable comparisons, the standard library has the
143`std::cmp::PartialOrd` trait that you can implement on types (see Appendix C,
144“Derivable Traits,” for more on this trait). You’ll learn how to specify that a
145generic type has a particular trait in the “Trait Bounds” section, but let’s
146first explore other ways of using generic type parameters.
147
148### In Struct Definitions
149
150We can also define structs to use a generic type parameter in one or more
151fields using the `<>` syntax. Listing 10-6 shows how to define a `Point<T>`
152struct to hold `x` and `y` coordinate values of any type:
cc61c64b
XL
153
154<span class="filename">Filename: src/main.rs</span>
155
156```rust
157struct Point<T> {
158 x: T,
159 y: T,
160}
161
162fn main() {
163 let integer = Point { x: 5, y: 10 };
164 let float = Point { x: 1.0, y: 4.0 };
165}
166```
167
0531ce1d 168<span class="caption">Listing 10-6: A `Point<T>` struct that holds `x` and `y`
cc61c64b
XL
169values of type `T`</span>
170
0531ce1d
XL
171The syntax for using generics in struct definitions is similar to that used in
172function definitions. First, we declare the name of the type parameter inside
173angle brackets just after the name of the struct. Then we can use the generic
174type in the struct definition where we would otherwise specify concrete data
175types.
cc61c64b 176
0531ce1d
XL
177Note that because we’ve only used one generic type to define `Point<T>`, this
178says that the `Point<T>` struct is generic over some type `T`, and the fields
179`x` and `y` are *both* that same type, whatever that type may be. This means
180that if we create an instance of a `Point<T>` that has values of different
181types, as in Listing 10-7, our code won’t compile:
cc61c64b
XL
182
183<span class="filename">Filename: src/main.rs</span>
184
185```rust,ignore
186struct Point<T> {
187 x: T,
188 y: T,
189}
190
191fn main() {
192 let wont_work = Point { x: 5, y: 4.0 };
193}
194```
195
196<span class="caption">Listing 10-7: The fields `x` and `y` must be the same
197type because both have the same generic data type `T`</span>
198
0531ce1d
XL
199In this example, when we assign the integer value `5` to `x`, we let the
200compiler know that the generic type `T` will be an integer for this instance of
201`Point<T>`. Then when we specify `4.0` for `y`, which we’ve defined to have the
202same type as `x`, we’ll get a type mismatch error like this:
cc61c64b
XL
203
204```text
205error[E0308]: mismatched types
0531ce1d 206 --> src/main.rs:7:38
cc61c64b
XL
207 |
2087 | let wont_work = Point { x: 5, y: 4.0 };
209 | ^^^ expected integral variable, found
0531ce1d 210floating-point variable
cc61c64b
XL
211 |
212 = note: expected type `{integer}`
0531ce1d 213 found type `{float}`
cc61c64b
XL
214```
215
0531ce1d
XL
216To define a `Point` struct where `x` and `y` are both generics but could have
217different types, we can use multiple generic type parameters. For example, in
218Listing 10-8, we can change the definition of `Point` to be generic over types
219`T` and `U` where `x` is of type `T` and `y` is of type `U`:
cc61c64b
XL
220
221<span class="filename">Filename: src/main.rs</span>
222
223```rust
224struct Point<T, U> {
225 x: T,
226 y: U,
227}
228
229fn main() {
230 let both_integer = Point { x: 5, y: 10 };
231 let both_float = Point { x: 1.0, y: 4.0 };
232 let integer_and_float = Point { x: 5, y: 4.0 };
233}
234```
235
0531ce1d
XL
236<span class="caption">Listing 10-8: A `Point<T, U>` generic over two types so
237that `x` and `y` can be values of different types</span>
cc61c64b 238
0531ce1d
XL
239Now all the instances of `Point` shown are allowed! You can use as many generic
240type parameters in a definition as you want, but using more than a few makes
241your code hard to read. When you need lots of generic types in your code, it
242could indicate that your code needs restructuring into smaller pieces.
cc61c64b 243
0531ce1d 244### In Enum Definitions
cc61c64b 245
0531ce1d
XL
246As we did with structs, we can define enums to hold generic data types in their
247variants. Let’s take another look at the `Option<T>` enum that the standard
248library provides that we used in Chapter 6:
cc61c64b
XL
249
250```rust
251enum Option<T> {
252 Some(T),
253 None,
254}
255```
256
0531ce1d
XL
257This definition should now make more sense to you. As you can see, `Option<T>`
258is an enum that is generic over type `T` and has two variants: `Some`, which
259holds one value of type `T`, and a `None` variant that doesn’t hold any value.
260By using the `Option<T>` enum, we can express the abstract concept of having an
261optional value, and because `Option<T>` is generic, we can use this abstraction
262no matter what the type of the optional value is.
cc61c64b
XL
263
264Enums can use multiple generic types as well. The definition of the `Result`
265enum that we used in Chapter 9 is one example:
266
267```rust
268enum Result<T, E> {
269 Ok(T),
270 Err(E),
271}
272```
273
0531ce1d
XL
274The `Result` enum is generic over two types, `T` and `E`, and has two variants:
275`Ok`, which holds a value of type `T`, and `Err`, which holds a value of type
276`E`. This definition makes it convenient to use the `Result` enum anywhere we
277have an operation that might succeed (return a value of some type `T`) or fail
278(return an error of some type `E`). In fact, this is what we used to open a
279file in Listing 9-3 where `T` was filled in with the type `std::fs::File` when
280the file was opened successfully and `E` was filled in with the type
281`std::io::Error` when there were problems opening the file.
cc61c64b
XL
282
283When you recognize situations in your code with multiple struct or enum
284definitions that differ only in the types of the values they hold, you can
0531ce1d 285avoid duplication by using generic types instead.
cc61c64b 286
0531ce1d 287### In Method Definitions
cc61c64b 288
0531ce1d
XL
289As we did in Chapter 5, we can implement methods on structs and enums that have
290generic types in their definitions. Listing 10-9 shows the `Point<T>` struct we
291defined in Listing 10-6 with a method named `x` implemented on it:
cc61c64b
XL
292
293<span class="filename">Filename: src/main.rs</span>
294
295```rust
296struct Point<T> {
297 x: T,
298 y: T,
299}
300
301impl<T> Point<T> {
302 fn x(&self) -> &T {
303 &self.x
304 }
305}
306
307fn main() {
308 let p = Point { x: 5, y: 10 };
309
310 println!("p.x = {}", p.x());
311}
312```
313
314<span class="caption">Listing 10-9: Implementing a method named `x` on the
0531ce1d
XL
315`Point<T>` struct that will return a reference to the `x` field of type
316`T`</span>
cc61c64b 317
0531ce1d
XL
318Here, we’ve defined a method named `x` on `Point<T>` that returns a reference
319to the data in the field `x`.
320
321Note that we have to declare `T` just after `impl` so we can use it to specify
322that we’re implementing methods on the type `Point<T>`. By declaring `T` as a
323generic type after `impl`, Rust can identify that the type in the angle
324brackets in `Point` is a generic type rather than a concrete type.
325
326We could, for example, implement methods only on `Point<f32>` instances rather
327than on `Point<T>` instances with any generic type. In Listing 10-10 we use the
328concrete type `f32`, meaning we don’t declare any types after `impl`:
cc61c64b 329
3b2f2976
XL
330```rust
331# struct Point<T> {
332# x: T,
333# y: T,
334# }
335#
336impl Point<f32> {
337 fn distance_from_origin(&self) -> f32 {
338 (self.x.powi(2) + self.y.powi(2)).sqrt()
339 }
340}
341```
342
0531ce1d
XL
343<span class="caption">Listing 10-10: An `impl` block that only applies to a
344struct with a particular concrete type for the generic type parameter `T`</span>
3b2f2976
XL
345
346This code means the type `Point<f32>` will have a method named
347`distance_from_origin`, and other instances of `Point<T>` where `T` is not of
0531ce1d
XL
348type `f32` will not have this method defined. The method measures how far our
349point is from the point at coordinates (0.0, 0.0) and uses mathematical
350operations that are only available for floating point types.
351
352Generic type parameters in a struct definition aren’t always the same as those
353you use in that struct’s method signatures. For example, Listing 10-11 defines
354the method `mixup` on the `Point<T, U>` struct from Listing 10-8. The method
355takes another `Point` as a parameter, which might have different types than the
356`self` `Point` we’re calling `mixup` on. The method creates a new `Point`
357instance with the `x` value from the `self` `Point` (of type `T`) and the `y`
358value from the passed-in `Point` (of type `W`):
cc61c64b
XL
359
360<span class="filename">Filename: src/main.rs</span>
361
362```rust
363struct Point<T, U> {
364 x: T,
365 y: U,
366}
367
368impl<T, U> Point<T, U> {
369 fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
370 Point {
371 x: self.x,
372 y: other.y,
373 }
374 }
375}
376
377fn main() {
378 let p1 = Point { x: 5, y: 10.4 };
379 let p2 = Point { x: "Hello", y: 'c'};
380
381 let p3 = p1.mixup(p2);
382
383 println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
384}
385```
386
3b2f2976
XL
387<span class="caption">Listing 10-11: Methods that use different generic types
388than their struct’s definition</span>
cc61c64b 389
3b2f2976 390In `main`, we’ve defined a `Point` that has an `i32` for `x` (with value `5`)
0531ce1d
XL
391and an `f64` for `y` (with value `10.4`). The `p2` variable is a `Point` struct
392that has a string slice for `x` (with value `"Hello"`) and a `char` for `y`
393(with value `c`). Calling `mixup` on `p1` with the argument `p2` gives us `p3`,
394which will have an `i32` for `x`, because `x` came from `p1`. The `p3` variable
395will have a `char` for `y`, because `y` came from `p2`. The `println!` macro
396call will print `p3.x = 5, p3.y = c`.
397
398The purpose of this example is to demonstrate a situation in which some generic
399parameters are declared with `impl` and some are declared with the method
400definition. Here, the generic parameters `T` and `U` are declared after `impl`,
401because they go with the struct definition. The generic parameters `V` and `W`
402are declared after `fn mixup`, because they’re only relevant to the method.
cc61c64b
XL
403
404### Performance of Code Using Generics
405
0531ce1d
XL
406You might be wondering whether there is a runtime cost when you’re using
407generic type parameters. The good news is that Rust implements generics in such
408a way that your code doesn’t run any slower using generic types than it would
409with concrete types.
cc61c64b 410
0531ce1d
XL
411Rust accomplishes this by performing monomorphization of the code that is using
412generics at compile time. *Monomorphization* is the process of turning generic
413code into specific code by filling in the concrete types that are used when
414compiled.
cc61c64b 415
0531ce1d
XL
416In this process, the compiler does the opposite of the steps we used to create
417the generic function in Listing 10-5: the compiler looks at all the places
418where generic code is called and generates code for the concrete types the
cc61c64b
XL
419generic code is called with.
420
0531ce1d
XL
421Let’s look at how this works with an example that uses the standard library’s
422`Option<T>` enum:
cc61c64b
XL
423
424```rust
425let integer = Some(5);
426let float = Some(5.0);
427```
428
0531ce1d
XL
429When Rust compiles this code, it performs monomorphization. During that
430process, the compiler reads the values that have been used in the instances of
431`Option<T>` and identifies two kinds of `Option<T>`: one is `i32` and the other
432is `f64`. As such, it expands the generic definition of `Option<T>` into
433`Option_i32` and `Option_f64`, thereby replacing the generic definition with
434the specific ones.
cc61c64b 435
0531ce1d
XL
436The monomorphized version of the code looks like the following. The generic
437`Option<T>` is replaced with the specific definitions created by the compiler:
cc61c64b
XL
438
439<span class="filename">Filename: src/main.rs</span>
440
441```rust
442enum Option_i32 {
443 Some(i32),
444 None,
445}
446
447enum Option_f64 {
448 Some(f64),
449 None,
450}
451
452fn main() {
453 let integer = Option_i32::Some(5);
454 let float = Option_f64::Some(5.0);
455}
456```
457
0531ce1d
XL
458Because Rust compiles generic code into code that specifies the type in each
459instance, we pay no runtime cost for using generics. When the code runs, it
460performs just like it would if we had duplicated each definition by hand. The
461process of monomorphization makes Rust’s generics extremely efficient at
462runtime.