11% Generics
22
33Sometimes, when writing a function or data type, we may want it to work for
4- multiple types of arguments. For example, remember our ` OptionalInt ` type?
4+ multiple types of arguments. Luckily, Rust has a feature that gives us a better
5+ way: generics. Generics are called ‘parametric polymorphism’ in type theory,
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’).
58
6- ``` {rust}
7- enum OptionalInt {
8- Value(i32),
9- Missing,
10- }
11- ```
12-
13- If we wanted to also have an ` OptionalFloat64 ` , we would need a new enum:
14-
15- ``` {rust}
16- enum OptionalFloat64 {
17- Valuef64(f64),
18- Missingf64,
19- }
20- ```
21-
22- This is really unfortunate. Luckily, Rust has a feature that gives us a better
23- way: generics. Generics are called * parametric polymorphism* in type theory,
24- which means that they are types or functions that have multiple forms (* poly*
25- is multiple, * morph* is form) over a given parameter (* parametric* ).
26-
27- Anyway, enough with type theory declarations, let's check out the generic form
28- of ` OptionalInt ` . It is actually provided by Rust itself, and looks like this:
9+ Anyway, enough with type theory, let’s check out some generic code. Rust’s
10+ standard library provides a type, ` Option<T> ` , that’s generic:
2911
3012``` rust
3113enum Option <T > {
@@ -34,59 +16,109 @@ enum Option<T> {
3416}
3517```
3618
37- The ` <T> ` part, which you' ve seen a few times before, indicates that this is
19+ The ` <T> ` part, which you’ ve seen a few times before, indicates that this is
3820a generic data type. Inside the declaration of our enum, wherever we see a ` T ` ,
39- we substitute that type for the same type used in the generic. Here' s an
21+ we substitute that type for the same type used in the generic. Here’ s an
4022example of using ` Option<T> ` , with some extra type annotations:
4123
42- ``` { rust}
24+ ``` rust
4325let x : Option <i32 > = Some (5 );
4426```
4527
4628In the type declaration, we say ` Option<i32> ` . Note how similar this looks to
4729` Option<T> ` . So, in this particular ` Option ` , ` T ` has the value of ` i32 ` . On
4830the right-hand side of the binding, we do make a ` Some(T) ` , where ` T ` is ` 5 ` .
49- Since that' s an ` i32 ` , the two sides match, and Rust is happy. If they didn' t
50- match, we' d get an error:
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:
5133
52- ``` { rust,ignore}
34+ ``` rust,ignore
5335let x: Option<f64> = Some(5);
5436// error: mismatched types: expected `core::option::Option<f64>`,
5537// found `core::option::Option<_>` (expected f64 but found integral variable)
5638```
5739
58- That doesn' t mean we can' t make ` Option<T> ` s that hold an ` f64 ` ! They just have to
59- match up:
40+ That doesn’ t mean we can’ t make ` Option<T> ` s that hold an ` f64 ` ! They just have
41+ to match up:
6042
61- ``` { rust}
43+ ``` rust
6244let x : Option <i32 > = Some (5 );
6345let y : Option <f64 > = Some (5.0f64 );
6446```
6547
6648This is just fine. One definition, multiple uses.
6749
68- Generics don't have to only be generic over one type. Consider Rust's built-in
69- ` Result<T, E> ` type:
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> ` :
7051
71- ``` { rust}
52+ ``` rust
7253enum Result <T , E > {
7354 Ok (T ),
7455 Err (E ),
7556}
7657```
7758
7859This type is generic over _ two_ types: ` T ` and ` E ` . By the way, the capital letters
79- can be any letter you' d like. We could define ` Result<T, E> ` as:
60+ can be any letter you’ d like. We could define ` Result<T, E> ` as:
8061
81- ``` { rust}
62+ ``` rust
8263enum Result <A , Z > {
8364 Ok (A ),
8465 Err (Z ),
8566}
8667```
8768
8869if we wanted to. Convention says that the first generic parameter should be
89- ` T ` , for ' type,' and that we use ` E ` for ' error.' Rust doesn' t care, however.
70+ ` T ` , for ‘ type’, and that we use ` E ` for ‘ error’. Rust doesn’ t care, however.
9071
9172The ` Result<T, E> ` type is intended to be used to return the result of a
92- computation, and to have the ability to return an error if it didn't work out.
73+ computation, and to have the ability to return an error if it didn’t work out.
74+
75+ ## Generic functions
76+
77+ We can write functions that take generic types with a similar syntax:
78+
79+ ``` rust
80+ fn takes_anything <T >(x : T ) {
81+ // do something with x
82+ }
83+ ```
84+
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 ` .”
87+
88+ Multiple arguments can have the same generic type:
89+
90+ ``` rust
91+ fn takes_two_of_the_same_things <T >(x : T , y : T ) {
92+ // ...
93+ }
94+ ```
95+
96+ We could write a version that takes multiple types:
97+
98+ ``` rust
99+ fn takes_two_things <T , U >(x : T , y : U ) {
100+ // ...
101+ }
102+ ```
103+
104+ Generic functions are most useful with ‘trait bounds’, which we’ll cover in the
105+ [ section on traits] [ traits ] .
106+
107+ [ traits ] : traits.html
108+
109+ ## Generic structs
110+
111+ You can store a generic type in a ` struct ` as well:
112+
113+ ```
114+ struct Point<T> {
115+ x: T,
116+ y: T,
117+ }
118+
119+ let int_origin = Point { x: 0, y: 0 };
120+ let float_origin = Point { x: 0.0, y: 0.0 };
121+ ```
122+
123+ Similarly to functions, the ` <T> ` is where we declare the generic parameters,
124+ and we then use ` x: T ` in the type declaration, too.
0 commit comments