rust/src/doc/trpl/generics.md
Leif Arne Storset 427736931b Copyedit generics.md and traits.md
Squashed at reviewer's request:

Add heading at the end of the introductory material
Spice up introductory paragraphs a bit
Use quotes instead of <code> for phrase
Remove "other" in "other restrictions" (it's not obvious that any other
restrictions have been mentioned)
"Default methods" is a second-level heading, but is not a subsection of
"Where clause"
Reword "Default methods" introduction: it's not the "last feature" on
this page
2015-07-30 21:49:14 +02:00

3.9 KiB
Raw Blame History

% Generics

Sometimes, when writing a function or data type, we may want it to work for multiple types of arguments. In Rust, we can do this with 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 type theory, lets check out some generic code. Rusts standard library provides a type, Option<T>, thats generic:

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

The <T> part, which youve 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. Heres an example of using Option<T>, with some extra type annotations:

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 make a Some(T), where T is 5. Since thats an i32, the two sides match, and Rust is happy. If they didnt match, wed get an error:

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 doesnt mean we cant make Option<T>s that hold an f64! They just have to match up:

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

This is just fine. One definition, multiple uses.

Generics dont have to only be generic over one type. Consider another type from Rusts standard library thats similar, Result<T, E>:

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 youd like. We could define Result<T, E> as:

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 doesnt 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 didnt work out.

Generic functions

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

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:

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

We could write a version that takes multiple types:

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

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.

When you want to add an implementation for the generic struct, you just declare the type parameter after the impl:

# struct Point<T> {
#     x: T,
#     y: T,
# }
#
impl<T> Point<T> {
    fn swap(&mut self) {
        std::mem::swap(&mut self.x, &mut self.y);
    }
}

So far youve seen generics that take absolutely any type. These are useful in many cases: youve already seen Option<T>, and later youll meet universal container types like Vec<T>. On the other hand, often you want to trade that flexibility for increased expressive power. Read about trait bounds to see why and how.