3 We can use generics to create definitions for items like function signatures or
4 structs, which we can then use with many different concrete data types. Let’s
5 first look at how to define functions, structs, enums, and methods using
6 generics. Then we’ll discuss how generics affect code performance.
8 ### In Function Definitions
10 When defining a function that uses generics, we place the generics in the
11 signature of the function where we would usually specify the data types of the
12 parameters and return value. Doing so makes our code more flexible and provides
13 more functionality to callers of our function while preventing code duplication.
15 Continuing with our `largest` function, Listing 10-4 shows two functions that
16 both find the largest value in a slice.
18 <span class="filename">Filename: src/main.rs</span>
21 fn largest_i32(list: &[i32]) -> i32 {
22 let mut largest = list[0];
24 for &item in list.iter() {
33 fn largest_char(list: &[char]) -> char {
34 let mut largest = list[0];
36 for &item in list.iter() {
46 let number_list = vec![34, 50, 25, 100, 65];
48 let result = largest_i32(&number_list);
49 println!("The largest number is {}", result);
50 # assert_eq!(result, 100);
52 let char_list = vec!['y', 'm', 'a', 'q'];
54 let result = largest_char(&char_list);
55 println!("The largest char is {}", result);
56 # assert_eq!(result, 'y');
60 <span class="caption">Listing 10-4: Two functions that differ only in their
61 names and the types in their signatures</span>
63 The `largest_i32` function is the one we extracted in Listing 10-3 that finds
64 the 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
66 the duplication by introducing a generic type parameter in a single function.
68 To parameterize the types in the new function we’ll define, we need to name the
69 type parameter, just as we do for the value parameters to a function. You can
70 use any identifier as a type parameter name. But we’ll use `T` because, by
71 convention, parameter names in Rust are short, often just a letter, and Rust’s
72 type-naming convention is CamelCase. Short for “type,” `T` is the default
73 choice of most Rust programmers.
75 When we use a parameter in the body of the function, we have to declare the
76 parameter name in the signature so the compiler knows what that name means.
77 Similarly, when we use a type parameter name in a function signature, we have
78 to declare the type parameter name before we use it. To define the generic
79 `largest` function, place type name declarations inside angle brackets, `<>`,
80 between the name of the function and the parameter list, like this:
83 fn largest<T>(list: &[T]) -> T {
86 We 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
88 of type `T`. The `largest` function will return a value of the same type `T`.
90 Listing 10-5 shows the combined `largest` function definition using the generic
91 data type in its signature. The listing also shows how we can call the function
92 with either a slice of `i32` values or `char` values. Note that this code won’t
93 compile yet, but we’ll fix it later in this chapter.
95 <span class="filename">Filename: src/main.rs</span>
97 ```rust,ignore,does_not_compile
98 fn largest<T>(list: &[T]) -> T {
99 let mut largest = list[0];
101 for &item in list.iter() {
111 let number_list = vec![34, 50, 25, 100, 65];
113 let result = largest(&number_list);
114 println!("The largest number is {}", result);
116 let char_list = vec!['y', 'm', 'a', 'q'];
118 let result = largest(&char_list);
119 println!("The largest char is {}", result);
123 <span class="caption">Listing 10-5: A definition of the `largest` function that
124 uses generic type parameters but doesn’t compile yet</span>
126 If we compile this code right now, we’ll get this error:
129 error[E0369]: binary operation `>` cannot be applied to type `T`
132 5 | if item > largest {
135 = note: an implementation of `std::cmp::PartialOrd` might be missing for `T`
138 The note mentions `std::cmp::PartialOrd`, which is a *trait*. We’ll talk about
139 traits 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
141 to compare values of type `T` in the body, we can only use types whose values
142 can 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 for more on this trait). You’ll learn how to specify that a generic type has a
145 particular trait in the [“Traits as Parameters”][traits-as-parameters]<!--
146 ignore --> section, but let’s first explore other ways of using generic type
149 ### In Struct Definitions
151 We can also define structs to use a generic type parameter in one or more
152 fields using the `<>` syntax. Listing 10-6 shows how to define a `Point<T>`
153 struct to hold `x` and `y` coordinate values of any type.
155 <span class="filename">Filename: src/main.rs</span>
164 let integer = Point { x: 5, y: 10 };
165 let float = Point { x: 1.0, y: 4.0 };
169 <span class="caption">Listing 10-6: A `Point<T>` struct that holds `x` and `y`
170 values of type `T`</span>
172 The syntax for using generics in struct definitions is similar to that used in
173 function definitions. First, we declare the name of the type parameter inside
174 angle brackets just after the name of the struct. Then we can use the generic
175 type in the struct definition where we would otherwise specify concrete data
178 Note that because we’ve used only one generic type to define `Point<T>`, this
179 definition says that the `Point<T>` struct is generic over some type `T`, and
180 the fields `x` and `y` are *both* that same type, whatever that type may be. If
181 we create an instance of a `Point<T>` that has values of different types, as in
182 Listing 10-7, our code won’t compile.
184 <span class="filename">Filename: src/main.rs</span>
186 ```rust,ignore,does_not_compile
193 let wont_work = Point { x: 5, y: 4.0 };
197 <span class="caption">Listing 10-7: The fields `x` and `y` must be the same
198 type because both have the same generic data type `T`.</span>
200 In this example, when we assign the integer value 5 to `x`, we let the
201 compiler know that the generic type `T` will be an integer for this instance of
202 `Point<T>`. Then when we specify 4.0 for `y`, which we’ve defined to have the
203 same type as `x`, we’ll get a type mismatch error like this:
206 error[E0308]: mismatched types
209 7 | let wont_work = Point { x: 5, y: 4.0 };
210 | ^^^ expected integral variable, found
211 floating-point variable
213 = note: expected type `{integer}`
217 To define a `Point` struct where `x` and `y` are both generics but could have
218 different types, we can use multiple generic type parameters. For example, in
219 Listing 10-8, we can change the definition of `Point` to be generic over types
220 `T` and `U` where `x` is of type `T` and `y` is of type `U`.
222 <span class="filename">Filename: src/main.rs</span>
231 let both_integer = Point { x: 5, y: 10 };
232 let both_float = Point { x: 1.0, y: 4.0 };
233 let integer_and_float = Point { x: 5, y: 4.0 };
237 <span class="caption">Listing 10-8: A `Point<T, U>` generic over two types so
238 that `x` and `y` can be values of different types</span>
240 Now all the instances of `Point` shown are allowed! You can use as many generic
241 type parameters in a definition as you want, but using more than a few makes
242 your code hard to read. When you need lots of generic types in your code, it
243 could indicate that your code needs restructuring into smaller pieces.
245 ### In Enum Definitions
247 As we did with structs, we can define enums to hold generic data types in their
248 variants. Let’s take another look at the `Option<T>` enum that the standard
249 library provides, which we used in Chapter 6:
258 This definition should now make more sense to you. As you can see, `Option<T>`
259 is an enum that is generic over type `T` and has two variants: `Some`, which
260 holds one value of type `T`, and a `None` variant that doesn’t hold any value.
261 By using the `Option<T>` enum, we can express the abstract concept of having an
262 optional value, and because `Option<T>` is generic, we can use this abstraction
263 no matter what the type of the optional value is.
265 Enums can use multiple generic types as well. The definition of the `Result`
266 enum that we used in Chapter 9 is one example:
275 The `Result` enum is generic over two types, `T` and `E`, and has two variants:
276 `Ok`, which holds a value of type `T`, and `Err`, which holds a value of type
277 `E`. This definition makes it convenient to use the `Result` enum anywhere we
278 have an operation that might succeed (return a value of some type `T`) or fail
279 (return an error of some type `E`). In fact, this is what we used to open a
280 file in Listing 9-3, where `T` was filled in with the type `std::fs::File` when
281 the file was opened successfully and `E` was filled in with the type
282 `std::io::Error` when there were problems opening the file.
284 When you recognize situations in your code with multiple struct or enum
285 definitions that differ only in the types of the values they hold, you can
286 avoid duplication by using generic types instead.
288 ### In Method Definitions
290 We can implement methods on structs and enums (as we did in Chapter 5) and use
291 generic types in their definitions, too. Listing 10-9 shows the `Point<T>`
292 struct we defined in Listing 10-6 with a method named `x` implemented on it.
294 <span class="filename">Filename: src/main.rs</span>
309 let p = Point { x: 5, y: 10 };
311 println!("p.x = {}", p.x());
315 <span class="caption">Listing 10-9: Implementing a method named `x` on the
316 `Point<T>` struct that will return a reference to the `x` field of type
319 Here, we’ve defined a method named `x` on `Point<T>` that returns a reference
320 to the data in the field `x`.
322 Note that we have to declare `T` just after `impl` so we can use it to specify
323 that we’re implementing methods on the type `Point<T>`. By declaring `T` as a
324 generic type after `impl`, Rust can identify that the type in the angle
325 brackets in `Point` is a generic type rather than a concrete type.
327 We could, for example, implement methods only on `Point<f32>` instances rather
328 than on `Point<T>` instances with any generic type. In Listing 10-10 we use the
329 concrete type `f32`, meaning we don’t declare any types after `impl`.
338 fn distance_from_origin(&self) -> f32 {
339 (self.x.powi(2) + self.y.powi(2)).sqrt()
344 <span class="caption">Listing 10-10: An `impl` block that only applies to a
345 struct with a particular concrete type for the generic type parameter `T`</span>
347 This code means the type `Point<f32>` will have a method named
348 `distance_from_origin` and other instances of `Point<T>` where `T` is not of
349 type `f32` will not have this method defined. The method measures how far our
350 point is from the point at coordinates (0.0, 0.0) and uses mathematical
351 operations that are available only for floating point types.
353 Generic type parameters in a struct definition aren’t always the same as those
354 you use in that struct’s method signatures. For example, Listing 10-11 defines
355 the method `mixup` on the `Point<T, U>` struct from Listing 10-8. The method
356 takes another `Point` as a parameter, which might have different types from the
357 `self` `Point` we’re calling `mixup` on. The method creates a new `Point`
358 instance with the `x` value from the `self` `Point` (of type `T`) and the `y`
359 value from the passed-in `Point` (of type `W`).
361 <span class="filename">Filename: src/main.rs</span>
369 impl<T, U> Point<T, U> {
370 fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
379 let p1 = Point { x: 5, y: 10.4 };
380 let p2 = Point { x: "Hello", y: 'c'};
382 let p3 = p1.mixup(p2);
384 println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
388 <span class="caption">Listing 10-11: A method that uses different generic types
389 from its struct’s definition</span>
391 In `main`, we’ve defined a `Point` that has an `i32` for `x` (with value `5`)
392 and an `f64` for `y` (with value `10.4`). The `p2` variable is a `Point` struct
393 that has a string slice for `x` (with value `"Hello"`) and a `char` for `y`
394 (with value `c`). Calling `mixup` on `p1` with the argument `p2` gives us `p3`,
395 which will have an `i32` for `x`, because `x` came from `p1`. The `p3` variable
396 will have a `char` for `y`, because `y` came from `p2`. The `println!` macro
397 call will print `p3.x = 5, p3.y = c`.
399 The purpose of this example is to demonstrate a situation in which some generic
400 parameters are declared with `impl` and some are declared with the method
401 definition. Here, the generic parameters `T` and `U` are declared after `impl`,
402 because they go with the struct definition. The generic parameters `V` and `W`
403 are declared after `fn mixup`, because they’re only relevant to the method.
405 ### Performance of Code Using Generics
407 You might be wondering whether there is a runtime cost when you’re using
408 generic type parameters. The good news is that Rust implements generics in such
409 a way that your code doesn’t run any slower using generic types than it would
412 Rust accomplishes this by performing monomorphization of the code that is using
413 generics at compile time. *Monomorphization* is the process of turning generic
414 code into specific code by filling in the concrete types that are used when
417 In this process, the compiler does the opposite of the steps we used to create
418 the generic function in Listing 10-5: the compiler looks at all the places
419 where generic code is called and generates code for the concrete types the
420 generic code is called with.
422 Let’s look at how this works with an example that uses the standard library’s
426 let integer = Some(5);
427 let float = Some(5.0);
430 When Rust compiles this code, it performs monomorphization. During that
431 process, the compiler reads the values that have been used in `Option<T>`
432 instances and identifies two kinds of `Option<T>`: one is `i32` and the other
433 is `f64`. As such, it expands the generic definition of `Option<T>` into
434 `Option_i32` and `Option_f64`, thereby replacing the generic definition with
437 The monomorphized version of the code looks like the following. The generic
438 `Option<T>` is replaced with the specific definitions created by the compiler:
440 <span class="filename">Filename: src/main.rs</span>
454 let integer = Option_i32::Some(5);
455 let float = Option_f64::Some(5.0);
459 Because Rust compiles generic code into code that specifies the type in each
460 instance, we pay no runtime cost for using generics. When the code runs, it
461 performs just as it would if we had duplicated each definition by hand. The
462 process of monomorphization makes Rust’s generics extremely efficient at
465 [traits-as-parameters]: ch10-02-traits.html#traits-as-parameters