2014-12-02 08:20:48 -06:00
|
|
|
|
% Generics
|
|
|
|
|
|
|
|
|
|
Sometimes, when writing a function or data type, we may want it to work for
|
2015-04-18 16:37:49 -05:00
|
|
|
|
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’).
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-18 16:37:49 -05:00
|
|
|
|
Anyway, enough with type theory, let’s check out some generic code. Rust’s
|
|
|
|
|
standard library provides a type, `Option<T>`, that’s generic:
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
enum Option<T> {
|
|
|
|
|
Some(T),
|
|
|
|
|
None,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2015-04-18 16:37:49 -05:00
|
|
|
|
The `<T>` part, which you’ve seen a few times before, indicates that this is
|
2014-12-02 08:20:48 -06:00
|
|
|
|
a generic data type. Inside the declaration of our enum, wherever we see a `T`,
|
2015-04-18 16:37:49 -05:00
|
|
|
|
we substitute that type for the same type used in the generic. Here’s an
|
2014-12-02 08:20:48 -06:00
|
|
|
|
example of using `Option<T>`, with some extra type annotations:
|
|
|
|
|
|
2015-04-18 16:37:49 -05:00
|
|
|
|
```rust
|
2015-01-13 09:40:18 -06:00
|
|
|
|
let x: Option<i32> = Some(5);
|
2014-12-02 08:20:48 -06:00
|
|
|
|
```
|
|
|
|
|
|
2015-01-13 09:40:18 -06:00
|
|
|
|
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`.
|
2015-04-18 16:37:49 -05:00
|
|
|
|
Since that’s an `i32`, the two sides match, and Rust is happy. If they didn’t
|
|
|
|
|
match, we’d get an error:
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-18 16:37:49 -05:00
|
|
|
|
```rust,ignore
|
2015-01-13 09:40:18 -06:00
|
|
|
|
let x: Option<f64> = Some(5);
|
|
|
|
|
// error: mismatched types: expected `core::option::Option<f64>`,
|
|
|
|
|
// found `core::option::Option<_>` (expected f64 but found integral variable)
|
2014-12-02 08:20:48 -06:00
|
|
|
|
```
|
|
|
|
|
|
2015-04-18 16:37:49 -05:00
|
|
|
|
That doesn’t mean we can’t make `Option<T>`s that hold an `f64`! They just have
|
|
|
|
|
to match up:
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-18 16:37:49 -05:00
|
|
|
|
```rust
|
2015-01-13 09:40:18 -06:00
|
|
|
|
let x: Option<i32> = Some(5);
|
2014-12-02 08:20:48 -06:00
|
|
|
|
let y: Option<f64> = Some(5.0f64);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is just fine. One definition, multiple uses.
|
|
|
|
|
|
2015-04-18 16:37:49 -05:00
|
|
|
|
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>`:
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-18 16:37:49 -05:00
|
|
|
|
```rust
|
2014-12-02 08:20:48 -06:00
|
|
|
|
enum Result<T, E> {
|
|
|
|
|
Ok(T),
|
|
|
|
|
Err(E),
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This type is generic over _two_ types: `T` and `E`. By the way, the capital letters
|
2015-04-18 16:37:49 -05:00
|
|
|
|
can be any letter you’d like. We could define `Result<T, E>` as:
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-18 16:37:49 -05:00
|
|
|
|
```rust
|
2015-01-20 10:36:27 -06:00
|
|
|
|
enum Result<A, Z> {
|
|
|
|
|
Ok(A),
|
|
|
|
|
Err(Z),
|
2014-12-02 08:20:48 -06:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
if we wanted to. Convention says that the first generic parameter should be
|
2015-04-18 16:37:49 -05:00
|
|
|
|
`T`, for ‘type’, and that we use `E` for ‘error’. Rust doesn’t care, however.
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-01-13 14:42:38 -06:00
|
|
|
|
The `Result<T, E>` type is intended to be used to return the result of a
|
2015-04-18 16:37:49 -05:00
|
|
|
|
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:
|
|
|
|
|
|
2015-05-18 13:56:00 -05:00
|
|
|
|
```rust
|
2015-04-18 16:37:49 -05:00
|
|
|
|
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.
|