]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | # Generic Types, Traits, and Lifetimes |
2 | ||
3 | Every programming language has tools for effectively handling the duplication | |
5e7ed085 FG |
4 | of concepts. In Rust, one such tool is *generics*: abstract stand-ins for |
5 | concrete types or other properties. We can express the behavior of generics or | |
6 | how they relate to other generics without knowing what will be in their place | |
7 | when compiling and running the code. | |
8 | ||
9 | Functions can take parameters of some generic type, instead of a concrete type | |
10 | like `i32` or `String`, in the same way a function takes parameters with | |
11 | unknown values to run the same code on multiple concrete values. In fact, we’ve | |
13cf67c4 XL |
12 | already used generics in Chapter 6 with `Option<T>`, Chapter 8 with `Vec<T>` |
13 | and `HashMap<K, V>`, and Chapter 9 with `Result<T, E>`. In this chapter, you’ll | |
14 | explore how to define your own types, functions, and methods with generics! | |
15 | ||
5e7ed085 FG |
16 | First, we’ll review how to extract a function to reduce code duplication. We’ll |
17 | then use the same technique to make a generic function from two functions that | |
13cf67c4 XL |
18 | differ only in the types of their parameters. We’ll also explain how to use |
19 | generic types in struct and enum definitions. | |
20 | ||
21 | Then you’ll learn how to use *traits* to define behavior in a generic way. You | |
5e7ed085 FG |
22 | can combine traits with generic types to constrain a generic type to accept |
23 | only those types that have a particular behavior, as opposed to just any type. | |
13cf67c4 | 24 | |
5e7ed085 | 25 | Finally, we’ll discuss *lifetimes*: a variety of generics that give the |
13cf67c4 | 26 | compiler information about how references relate to each other. Lifetimes allow |
5e7ed085 FG |
27 | us to give the compiler enough information about borrowed values so that it can |
28 | ensure references will be valid in more situations than it could without our | |
29 | help. | |
13cf67c4 XL |
30 | |
31 | ## Removing Duplication by Extracting a Function | |
32 | ||
5e7ed085 FG |
33 | Generics allow us to replace specific types with a placeholder that represents |
34 | multiple types to remove code duplication. Before diving into generics syntax, | |
35 | then, let’s first look at how to remove duplication in a way that doesn’t | |
36 | involve generic types by extracting a function that replaces specific values | |
37 | with a placeholder that represents multiple values. Then we’ll apply the same | |
38 | technique to extract a generic function! By looking at how to recognize | |
39 | duplicated code you can extract into a function, you’ll start to recognize | |
40 | duplicated code that can use generics. | |
13cf67c4 | 41 | |
5e7ed085 FG |
42 | We begin with the short program in Listing 10-1 that finds the largest number |
43 | in a list. | |
13cf67c4 XL |
44 | |
45 | <span class="filename">Filename: src/main.rs</span> | |
46 | ||
47 | ```rust | |
74b04a01 | 48 | {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-01/src/main.rs:here}} |
13cf67c4 XL |
49 | ``` |
50 | ||
5e7ed085 FG |
51 | <span class="caption">Listing 10-1: Finding the largest number in a list of |
52 | numbers</span> | |
13cf67c4 | 53 | |
923072b8 FG |
54 | We store a list of integers in the variable `number_list` and place a reference |
55 | to the first number in the list in a variable named `largest`. We then iterate | |
56 | through all the numbers in the list, and if the current number is greater than | |
57 | the number stored in `largest`, replace the reference in that variable. | |
58 | However, if the current number is less than or equal to the largest number seen | |
59 | so far, the variable doesn’t change, and the code moves on to the next number | |
60 | in the list. After considering all the numbers in the list, `largest` should | |
61 | refer to the largest number, which in this case is 100. | |
13cf67c4 | 62 | |
5e7ed085 FG |
63 | We've now been tasked with finding the largest number in two different lists of |
64 | numbers. To do so, we can choose to duplicate the code in Listing 10-1 and use | |
65 | the same logic at two different places in the program, as shown in Listing 10-2. | |
13cf67c4 XL |
66 | |
67 | <span class="filename">Filename: src/main.rs</span> | |
68 | ||
69 | ```rust | |
74b04a01 | 70 | {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-02/src/main.rs}} |
13cf67c4 XL |
71 | ``` |
72 | ||
73 | <span class="caption">Listing 10-2: Code to find the largest number in *two* | |
74 | lists of numbers</span> | |
75 | ||
76 | Although this code works, duplicating code is tedious and error prone. We also | |
5e7ed085 FG |
77 | have to remember to update the code in multiple places when we want to change |
78 | it. | |
13cf67c4 | 79 | |
5e7ed085 FG |
80 | To eliminate this duplication, we’ll create an abstraction by defining a |
81 | function that operates on any list of integers passed in a parameter. This | |
13cf67c4 XL |
82 | solution makes our code clearer and lets us express the concept of finding the |
83 | largest number in a list abstractly. | |
84 | ||
5e7ed085 FG |
85 | In Listing 10-3, we extract the code that finds the largest number into a |
86 | function named `largest`. Then we call the function to find the largest number | |
87 | in the two lists from Listing 10-2. We could also use the function on any other | |
88 | list of `i32` values we might have in the future. | |
13cf67c4 XL |
89 | |
90 | <span class="filename">Filename: src/main.rs</span> | |
91 | ||
92 | ```rust | |
74b04a01 | 93 | {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-03/src/main.rs:here}} |
13cf67c4 XL |
94 | ``` |
95 | ||
96 | <span class="caption">Listing 10-3: Abstracted code to find the largest number | |
97 | in two lists</span> | |
98 | ||
99 | The `largest` function has a parameter called `list`, which represents any | |
5e7ed085 FG |
100 | concrete slice of `i32` values we might pass into the function. As a result, |
101 | when we call the function, the code runs on the specific values that we pass | |
923072b8 | 102 | in. |
13cf67c4 | 103 | |
923072b8 | 104 | In summary, here are the steps we took to change the code from Listing 10-2 to |
13cf67c4 XL |
105 | Listing 10-3: |
106 | ||
107 | 1. Identify duplicate code. | |
108 | 2. Extract the duplicate code into the body of the function and specify the | |
109 | inputs and return values of that code in the function signature. | |
110 | 3. Update the two instances of duplicated code to call the function instead. | |
111 | ||
5e7ed085 FG |
112 | Next, we’ll use these same steps with generics to reduce code duplication. In |
113 | the same way that the function body can operate on an abstract `list` instead | |
114 | of specific values, generics allow code to operate on abstract types. | |
13cf67c4 XL |
115 | |
116 | For example, say we had two functions: one that finds the largest item in a | |
117 | slice of `i32` values and one that finds the largest item in a slice of `char` | |
118 | values. How would we eliminate that duplication? Let’s find out! |