]> git.proxmox.com Git - rustc.git/blame - src/doc/book/src/ch10-01-syntax.md
New upstream version 1.43.0+dfsg1
[rustc.git] / src / doc / book / src / ch10-01-syntax.md
CommitLineData
13cf67c4
XL
1## Generic Data Types
2
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.
7
8### In Function Definitions
9
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.
14
15Continuing with our `largest` function, Listing 10-4 shows two functions that
16both find the largest value in a slice.
17
18<span class="filename">Filename: src/main.rs</span>
19
20```rust
74b04a01 21{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-04/src/main.rs:here}}
13cf67c4
XL
22```
23
24<span class="caption">Listing 10-4: Two functions that differ only in their
25names and the types in their signatures</span>
26
27The `largest_i32` function is the one we extracted in Listing 10-3 that finds
28the largest `i32` in a slice. The `largest_char` function finds the largest
29`char` in a slice. The function bodies have the same code, so let’s eliminate
30the duplication by introducing a generic type parameter in a single function.
31
32To parameterize the types in the new function we’ll define, we need to name the
33type parameter, just as we do for the value parameters to a function. You can
34use any identifier as a type parameter name. But we’ll use `T` because, by
35convention, parameter names in Rust are short, often just a letter, and Rust’s
36type-naming convention is CamelCase. Short for “type,” `T` is the default
37choice of most Rust programmers.
38
39When we use a parameter in the body of the function, we have to declare the
40parameter name in the signature so the compiler knows what that name means.
41Similarly, when we use a type parameter name in a function signature, we have
42to declare the type parameter name before we use it. To define the generic
43`largest` function, place type name declarations inside angle brackets, `<>`,
44between the name of the function and the parameter list, like this:
45
46```rust,ignore
47fn largest<T>(list: &[T]) -> T {
48```
49
50We read this definition as: the function `largest` is generic over some type
51`T`. This function has one parameter named `list`, which is a slice of values
52of type `T`. The `largest` function will return a value of the same type `T`.
53
54Listing 10-5 shows the combined `largest` function definition using the generic
55data type in its signature. The listing also shows how we can call the function
56with either a slice of `i32` values or `char` values. Note that this code won’t
57compile yet, but we’ll fix it later in this chapter.
58
59<span class="filename">Filename: src/main.rs</span>
60
61```rust,ignore,does_not_compile
74b04a01 62{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-05/src/main.rs}}
13cf67c4
XL
63```
64
65<span class="caption">Listing 10-5: A definition of the `largest` function that
66uses generic type parameters but doesn’t compile yet</span>
67
68If we compile this code right now, we’ll get this error:
69
70```text
74b04a01 71{{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-05/output.txt}}
13cf67c4
XL
72```
73
74The note mentions `std::cmp::PartialOrd`, which is a *trait*. We’ll talk about
75traits in the next section. For now, this error states that the body of
76`largest` won’t work for all possible types that `T` could be. Because we want
77to compare values of type `T` in the body, we can only use types whose values
78can be ordered. To enable comparisons, the standard library has the
79`std::cmp::PartialOrd` trait that you can implement on types (see Appendix C
80for more on this trait). You’ll learn how to specify that a generic type has a
9fa01778
XL
81particular trait in the [“Traits as Parameters”][traits-as-parameters]<!--
82ignore --> section, but let’s first explore other ways of using generic type
83parameters.
13cf67c4
XL
84
85### In Struct Definitions
86
87We can also define structs to use a generic type parameter in one or more
88fields using the `<>` syntax. Listing 10-6 shows how to define a `Point<T>`
89struct to hold `x` and `y` coordinate values of any type.
90
91<span class="filename">Filename: src/main.rs</span>
92
93```rust
74b04a01 94{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-06/src/main.rs}}
13cf67c4
XL
95```
96
97<span class="caption">Listing 10-6: A `Point<T>` struct that holds `x` and `y`
98values of type `T`</span>
99
100The syntax for using generics in struct definitions is similar to that used in
101function definitions. First, we declare the name of the type parameter inside
102angle brackets just after the name of the struct. Then we can use the generic
103type in the struct definition where we would otherwise specify concrete data
104types.
105
106Note that because we’ve used only one generic type to define `Point<T>`, this
107definition says that the `Point<T>` struct is generic over some type `T`, and
108the fields `x` and `y` are *both* that same type, whatever that type may be. If
109we create an instance of a `Point<T>` that has values of different types, as in
110Listing 10-7, our code won’t compile.
111
112<span class="filename">Filename: src/main.rs</span>
113
114```rust,ignore,does_not_compile
74b04a01 115{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-07/src/main.rs}}
13cf67c4
XL
116```
117
118<span class="caption">Listing 10-7: The fields `x` and `y` must be the same
119type because both have the same generic data type `T`.</span>
120
121In this example, when we assign the integer value 5 to `x`, we let the
122compiler know that the generic type `T` will be an integer for this instance of
123`Point<T>`. Then when we specify 4.0 for `y`, which we’ve defined to have the
124same type as `x`, we’ll get a type mismatch error like this:
125
126```text
74b04a01 127{{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-07/output.txt}}
13cf67c4
XL
128```
129
130To define a `Point` struct where `x` and `y` are both generics but could have
131different types, we can use multiple generic type parameters. For example, in
132Listing 10-8, we can change the definition of `Point` to be generic over types
133`T` and `U` where `x` is of type `T` and `y` is of type `U`.
134
135<span class="filename">Filename: src/main.rs</span>
136
137```rust
74b04a01 138{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-08/src/main.rs}}
13cf67c4
XL
139```
140
141<span class="caption">Listing 10-8: A `Point<T, U>` generic over two types so
142that `x` and `y` can be values of different types</span>
143
144Now all the instances of `Point` shown are allowed! You can use as many generic
145type parameters in a definition as you want, but using more than a few makes
146your code hard to read. When you need lots of generic types in your code, it
147could indicate that your code needs restructuring into smaller pieces.
148
149### In Enum Definitions
150
151As we did with structs, we can define enums to hold generic data types in their
152variants. Let’s take another look at the `Option<T>` enum that the standard
153library provides, which we used in Chapter 6:
154
155```rust
156enum Option<T> {
157 Some(T),
158 None,
159}
160```
161
162This definition should now make more sense to you. As you can see, `Option<T>`
163is an enum that is generic over type `T` and has two variants: `Some`, which
164holds one value of type `T`, and a `None` variant that doesn’t hold any value.
165By using the `Option<T>` enum, we can express the abstract concept of having an
166optional value, and because `Option<T>` is generic, we can use this abstraction
167no matter what the type of the optional value is.
168
169Enums can use multiple generic types as well. The definition of the `Result`
170enum that we used in Chapter 9 is one example:
171
172```rust
173enum Result<T, E> {
174 Ok(T),
175 Err(E),
176}
177```
178
179The `Result` enum is generic over two types, `T` and `E`, and has two variants:
180`Ok`, which holds a value of type `T`, and `Err`, which holds a value of type
181`E`. This definition makes it convenient to use the `Result` enum anywhere we
182have an operation that might succeed (return a value of some type `T`) or fail
183(return an error of some type `E`). In fact, this is what we used to open a
184file in Listing 9-3, where `T` was filled in with the type `std::fs::File` when
185the file was opened successfully and `E` was filled in with the type
186`std::io::Error` when there were problems opening the file.
187
188When you recognize situations in your code with multiple struct or enum
189definitions that differ only in the types of the values they hold, you can
190avoid duplication by using generic types instead.
191
192### In Method Definitions
193
194We can implement methods on structs and enums (as we did in Chapter 5) and use
195generic types in their definitions, too. Listing 10-9 shows the `Point<T>`
196struct we defined in Listing 10-6 with a method named `x` implemented on it.
197
198<span class="filename">Filename: src/main.rs</span>
199
200```rust
74b04a01 201{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-09/src/main.rs}}
13cf67c4
XL
202```
203
204<span class="caption">Listing 10-9: Implementing a method named `x` on the
205`Point<T>` struct that will return a reference to the `x` field of type
206`T`</span>
207
208Here, we’ve defined a method named `x` on `Point<T>` that returns a reference
209to the data in the field `x`.
210
211Note that we have to declare `T` just after `impl` so we can use it to specify
212that we’re implementing methods on the type `Point<T>`. By declaring `T` as a
213generic type after `impl`, Rust can identify that the type in the angle
214brackets in `Point` is a generic type rather than a concrete type.
215
216We could, for example, implement methods only on `Point<f32>` instances rather
217than on `Point<T>` instances with any generic type. In Listing 10-10 we use the
218concrete type `f32`, meaning we don’t declare any types after `impl`.
219
74b04a01
XL
220<span class="filename">Filename: src/main.rs</span>
221
13cf67c4 222```rust
74b04a01 223{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-10/src/main.rs:here}}
13cf67c4
XL
224```
225
226<span class="caption">Listing 10-10: An `impl` block that only applies to a
227struct with a particular concrete type for the generic type parameter `T`</span>
228
229This code means the type `Point<f32>` will have a method named
230`distance_from_origin` and other instances of `Point<T>` where `T` is not of
231type `f32` will not have this method defined. The method measures how far our
232point is from the point at coordinates (0.0, 0.0) and uses mathematical
233operations that are available only for floating point types.
234
235Generic type parameters in a struct definition aren’t always the same as those
236you use in that struct’s method signatures. For example, Listing 10-11 defines
237the method `mixup` on the `Point<T, U>` struct from Listing 10-8. The method
532ac7d7 238takes another `Point` as a parameter, which might have different types from the
13cf67c4
XL
239`self` `Point` we’re calling `mixup` on. The method creates a new `Point`
240instance with the `x` value from the `self` `Point` (of type `T`) and the `y`
241value from the passed-in `Point` (of type `W`).
242
243<span class="filename">Filename: src/main.rs</span>
244
245```rust
74b04a01 246{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-11/src/main.rs}}
13cf67c4
XL
247```
248
249<span class="caption">Listing 10-11: A method that uses different generic types
532ac7d7 250from its struct’s definition</span>
13cf67c4
XL
251
252In `main`, we’ve defined a `Point` that has an `i32` for `x` (with value `5`)
253and an `f64` for `y` (with value `10.4`). The `p2` variable is a `Point` struct
254that has a string slice for `x` (with value `"Hello"`) and a `char` for `y`
255(with value `c`). Calling `mixup` on `p1` with the argument `p2` gives us `p3`,
256which will have an `i32` for `x`, because `x` came from `p1`. The `p3` variable
257will have a `char` for `y`, because `y` came from `p2`. The `println!` macro
258call will print `p3.x = 5, p3.y = c`.
259
260The purpose of this example is to demonstrate a situation in which some generic
261parameters are declared with `impl` and some are declared with the method
262definition. Here, the generic parameters `T` and `U` are declared after `impl`,
263because they go with the struct definition. The generic parameters `V` and `W`
264are declared after `fn mixup`, because they’re only relevant to the method.
265
266### Performance of Code Using Generics
267
268You might be wondering whether there is a runtime cost when you’re using
269generic type parameters. The good news is that Rust implements generics in such
270a way that your code doesn’t run any slower using generic types than it would
271with concrete types.
272
273Rust accomplishes this by performing monomorphization of the code that is using
274generics at compile time. *Monomorphization* is the process of turning generic
275code into specific code by filling in the concrete types that are used when
276compiled.
277
278In this process, the compiler does the opposite of the steps we used to create
279the generic function in Listing 10-5: the compiler looks at all the places
280where generic code is called and generates code for the concrete types the
281generic code is called with.
282
283Let’s look at how this works with an example that uses the standard library’s
284`Option<T>` enum:
285
286```rust
287let integer = Some(5);
288let float = Some(5.0);
289```
290
291When Rust compiles this code, it performs monomorphization. During that
292process, the compiler reads the values that have been used in `Option<T>`
293instances and identifies two kinds of `Option<T>`: one is `i32` and the other
294is `f64`. As such, it expands the generic definition of `Option<T>` into
295`Option_i32` and `Option_f64`, thereby replacing the generic definition with
296the specific ones.
297
298The monomorphized version of the code looks like the following. The generic
299`Option<T>` is replaced with the specific definitions created by the compiler:
300
301<span class="filename">Filename: src/main.rs</span>
302
303```rust
304enum Option_i32 {
305 Some(i32),
306 None,
307}
308
309enum Option_f64 {
310 Some(f64),
311 None,
312}
313
314fn main() {
315 let integer = Option_i32::Some(5);
316 let float = Option_f64::Some(5.0);
317}
318```
319
320Because Rust compiles generic code into code that specifies the type in each
321instance, we pay no runtime cost for using generics. When the code runs, it
322performs just as it would if we had duplicated each definition by hand. The
323process of monomorphization makes Rust’s generics extremely efficient at
324runtime.
9fa01778
XL
325
326[traits-as-parameters]: ch10-02-traits.html#traits-as-parameters