]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | % Generics |
2 | ||
3 | Sometimes, when writing a function or data type, we may want it to work for | |
62682a34 SL |
4 | multiple types of arguments. In Rust, we can do this with generics. |
5 | Generics are called ‘parametric polymorphism’ in type theory, | |
bd371182 AL |
6 | which means that they are types or functions that have multiple forms (‘poly’ |
7 | is multiple, ‘morph’ is form) over a given parameter (‘parametric’). | |
1a4d82fc | 8 | |
e9174d1e | 9 | Anyway, enough type theory, let’s check out some generic code. Rust’s |
bd371182 | 10 | standard library provides a type, `Option<T>`, that’s generic: |
1a4d82fc JJ |
11 | |
12 | ```rust | |
13 | enum Option<T> { | |
14 | Some(T), | |
15 | None, | |
16 | } | |
17 | ``` | |
18 | ||
bd371182 | 19 | The `<T>` part, which you’ve seen a few times before, indicates that this is |
e9174d1e | 20 | a generic data type. Inside the declaration of our `enum`, wherever we see a `T`, |
bd371182 | 21 | we substitute that type for the same type used in the generic. Here’s an |
1a4d82fc JJ |
22 | example of using `Option<T>`, with some extra type annotations: |
23 | ||
bd371182 | 24 | ```rust |
85aaf69f | 25 | let x: Option<i32> = Some(5); |
1a4d82fc JJ |
26 | ``` |
27 | ||
85aaf69f SL |
28 | In the type declaration, we say `Option<i32>`. Note how similar this looks to |
29 | `Option<T>`. So, in this particular `Option`, `T` has the value of `i32`. On | |
e9174d1e | 30 | the right-hand side of the binding, we make a `Some(T)`, where `T` is `5`. |
bd371182 AL |
31 | Since that’s an `i32`, the two sides match, and Rust is happy. If they didn’t |
32 | match, we’d get an error: | |
1a4d82fc | 33 | |
bd371182 | 34 | ```rust,ignore |
85aaf69f SL |
35 | let x: Option<f64> = Some(5); |
36 | // error: mismatched types: expected `core::option::Option<f64>`, | |
37 | // found `core::option::Option<_>` (expected f64 but found integral variable) | |
1a4d82fc JJ |
38 | ``` |
39 | ||
9cc50fc6 | 40 | That doesn’t mean we can’t make `Option<T>`s that hold an `f64`! They have |
bd371182 | 41 | to match up: |
1a4d82fc | 42 | |
bd371182 | 43 | ```rust |
85aaf69f | 44 | let x: Option<i32> = Some(5); |
1a4d82fc JJ |
45 | let y: Option<f64> = Some(5.0f64); |
46 | ``` | |
47 | ||
48 | This is just fine. One definition, multiple uses. | |
49 | ||
bd371182 | 50 | Generics don’t have to only be generic over one type. Consider another type from Rust’s standard library that’s similar, `Result<T, E>`: |
1a4d82fc | 51 | |
bd371182 | 52 | ```rust |
1a4d82fc JJ |
53 | enum Result<T, E> { |
54 | Ok(T), | |
55 | Err(E), | |
56 | } | |
57 | ``` | |
58 | ||
59 | This type is generic over _two_ types: `T` and `E`. By the way, the capital letters | |
bd371182 | 60 | can be any letter you’d like. We could define `Result<T, E>` as: |
1a4d82fc | 61 | |
bd371182 | 62 | ```rust |
85aaf69f SL |
63 | enum Result<A, Z> { |
64 | Ok(A), | |
65 | Err(Z), | |
1a4d82fc JJ |
66 | } |
67 | ``` | |
68 | ||
69 | if we wanted to. Convention says that the first generic parameter should be | |
bd371182 | 70 | `T`, for ‘type’, and that we use `E` for ‘error’. Rust doesn’t care, however. |
1a4d82fc | 71 | |
85aaf69f | 72 | The `Result<T, E>` type is intended to be used to return the result of a |
bd371182 | 73 | computation, and to have the ability to return an error if it didn’t work out. |
1a4d82fc | 74 | |
bd371182 | 75 | ## Generic functions |
1a4d82fc | 76 | |
bd371182 | 77 | We can write functions that take generic types with a similar syntax: |
1a4d82fc | 78 | |
bd371182 AL |
79 | ```rust |
80 | fn takes_anything<T>(x: T) { | |
476ff2be | 81 | // Do something with `x`. |
1a4d82fc JJ |
82 | } |
83 | ``` | |
84 | ||
bd371182 AL |
85 | The syntax has two parts: the `<T>` says “this function is generic over one |
86 | type, `T`”, and the `x: T` says “x has the type `T`.” | |
1a4d82fc | 87 | |
bd371182 | 88 | Multiple arguments can have the same generic type: |
1a4d82fc | 89 | |
bd371182 AL |
90 | ```rust |
91 | fn takes_two_of_the_same_things<T>(x: T, y: T) { | |
92 | // ... | |
1a4d82fc JJ |
93 | } |
94 | ``` | |
95 | ||
bd371182 | 96 | We could write a version that takes multiple types: |
1a4d82fc | 97 | |
bd371182 AL |
98 | ```rust |
99 | fn takes_two_things<T, U>(x: T, y: U) { | |
100 | // ... | |
1a4d82fc JJ |
101 | } |
102 | ``` | |
103 | ||
bd371182 | 104 | ## Generic structs |
1a4d82fc | 105 | |
bd371182 | 106 | You can store a generic type in a `struct` as well: |
1a4d82fc | 107 | |
62682a34 | 108 | ```rust |
bd371182 AL |
109 | struct Point<T> { |
110 | x: T, | |
111 | y: T, | |
112 | } | |
1a4d82fc | 113 | |
bd371182 AL |
114 | let int_origin = Point { x: 0, y: 0 }; |
115 | let float_origin = Point { x: 0.0, y: 0.0 }; | |
116 | ``` | |
1a4d82fc | 117 | |
e9174d1e | 118 | Similar to functions, the `<T>` is where we declare the generic parameters, |
bd371182 | 119 | and we then use `x: T` in the type declaration, too. |
e9174d1e | 120 | |
9cc50fc6 | 121 | When you want to add an implementation for the generic `struct`, you |
e9174d1e SL |
122 | declare the type parameter after the `impl`: |
123 | ||
124 | ```rust | |
125 | # struct Point<T> { | |
126 | # x: T, | |
127 | # y: T, | |
128 | # } | |
129 | # | |
130 | impl<T> Point<T> { | |
131 | fn swap(&mut self) { | |
132 | std::mem::swap(&mut self.x, &mut self.y); | |
133 | } | |
134 | } | |
135 | ``` | |
136 | ||
137 | So far you’ve seen generics that take absolutely any type. These are useful in | |
138 | many cases: you’ve already seen `Option<T>`, and later you’ll meet universal | |
139 | container types like [`Vec<T>`][Vec]. On the other hand, often you want to | |
140 | trade that flexibility for increased expressive power. Read about [trait | |
141 | bounds][traits] to see why and how. | |
142 | ||
143 | [traits]: traits.html | |
144 | [Vec]: ../std/vec/struct.Vec.html |