summary refs log tree commit diff
path: root/src/doc/trpl/generics.md
blob: 517a6e6064253295a6292e1a6b2a3e0147f5b2bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
% Generics

Sometimes, when writing a function or data type, we may want it to work for
multiple types of arguments. Luckily, Rust has a feature that gives us a better
way: generics. Generics are called ‘parametric polymorphism’ in type theory,
which means that they are types or functions that have multiple forms (‘poly’
is multiple, ‘morph’ is form) over a given parameter (‘parametric’).

Anyway, enough with type theory, let’s check out some generic code. Rust’s
standard library provides a type, `Option<T>`, that’s generic:

```rust
enum Option<T> {
    Some(T),
    None,
}
```

The `<T>` part, which you’ve seen a few times before, indicates that this is
a generic data type. Inside the declaration of our enum, wherever we see a `T`,
we substitute that type for the same type used in the generic. Here’s an
example of using `Option<T>`, with some extra type annotations:

```rust
let x: Option<i32> = Some(5);
```

In the type declaration, we say `Option<i32>`. Note how similar this looks to
`Option<T>`. So, in this particular `Option`, `T` has the value of `i32`. On
the right-hand side of the binding, we do make a `Some(T)`, where `T` is `5`.
Since that’s an `i32`, the two sides match, and Rust is happy. If they didn’t
match, we’d get an error:

```rust,ignore
let x: Option<f64> = Some(5);
// error: mismatched types: expected `core::option::Option<f64>`,
// found `core::option::Option<_>` (expected f64 but found integral variable)
```

That doesn’t mean we can’t make `Option<T>`s that hold an `f64`! They just have
to match up:

```rust
let x: Option<i32> = Some(5);
let y: Option<f64> = Some(5.0f64);
```

This is just fine. One definition, multiple uses.

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>`:

```rust
enum Result<T, E> {
    Ok(T),
    Err(E),
}
```

This type is generic over _two_ types: `T` and `E`. By the way, the capital letters
can be any letter you’d like. We could define `Result<T, E>` as:

```rust
enum Result<A, Z> {
    Ok(A),
    Err(Z),
}
```

if we wanted to. Convention says that the first generic parameter should be
`T`, for ‘type’, and that we use `E` for ‘error’. Rust doesn’t care, however.

The `Result<T, E>` type is intended to be used to return the result of a
computation, and to have the ability to return an error if it didn’t work out.

## Generic functions

We can write functions that take generic types with a similar syntax:

```rust
fn takes_anything<T>(x: T) {
    // do something with x
}
```

The syntax has two parts: the `<T>` says “this function is generic over one
type, `T`”, and the `x: T` says “x has the type `T`.”

Multiple arguments can have the same generic type:

```rust
fn takes_two_of_the_same_things<T>(x: T, y: T) {
    // ...
}
```

We could write a version that takes multiple types:

```rust
fn takes_two_things<T, U>(x: T, y: U) {
    // ...
}
```

Generic functions are most useful with ‘trait bounds’, which we’ll cover in the
[section on traits][traits].

[traits]: traits.html

## Generic structs

You can store a generic type in a `struct` as well:

```
struct Point<T> {
    x: T,
    y: T,
}

let int_origin = Point { x: 0, y: 0 };
let float_origin = Point { x: 0.0, y: 0.0 };
```

Similarly to functions, the `<T>` is where we declare the generic parameters,
and we then use `x: T` in the type declaration, too.