1. 1. Introduction
  2. 2. Getting Started
  3. 3. Tutorial: Guessing Game
  4. 4. Syntax and Semantics
    1. 4.1. Variable Bindings
    2. 4.2. Functions
    3. 4.3. Primitive Types
    4. 4.4. Comments
    5. 4.5. if
    6. 4.6. Loops
    7. 4.7. Vectors
    8. 4.8. Ownership
    9. 4.9. References and Borrowing
    10. 4.10. Lifetimes
    11. 4.11. Mutability
    12. 4.12. Structs
    13. 4.13. Enums
    14. 4.14. Match
    15. 4.15. Patterns
    16. 4.16. Method Syntax
    17. 4.17. Strings
    18. 4.18. Generics
    19. 4.19. Traits
    20. 4.20. Drop
    21. 4.21. if let
    22. 4.22. Trait Objects
    23. 4.23. Closures
    24. 4.24. Universal Function Call Syntax
    25. 4.25. Crates and Modules
    26. 4.26. `const` and `static`
    27. 4.27. Attributes
    28. 4.28. `type` aliases
    29. 4.29. Casting between types
    30. 4.30. Associated Types
    31. 4.31. Unsized Types
    32. 4.32. Operators and Overloading
    33. 4.33. Deref coercions
    34. 4.34. Macros
    35. 4.35. Raw Pointers
    36. 4.36. `unsafe`
  5. 5. Effective Rust
    1. 5.1. The Stack and the Heap
    2. 5.2. Testing
    3. 5.3. Conditional Compilation
    4. 5.4. Documentation
    5. 5.5. Iterators
    6. 5.6. Concurrency
    7. 5.7. Error Handling
    8. 5.8. Choosing your Guarantees
    9. 5.9. FFI
    10. 5.10. Borrow and AsRef
    11. 5.11. Release Channels
    12. 5.12. Using Rust without the standard library
  6. 6. Nightly Rust
    1. 6.1. Compiler Plugins
    2. 6.2. Inline Assembly
    3. 6.3. No stdlib
    4. 6.4. Intrinsics
    5. 6.5. Lang items
    6. 6.6. Advanced linking
    7. 6.7. Benchmark Tests
    8. 6.8. Box Syntax and Patterns
    9. 6.9. Slice Patterns
    10. 6.10. Associated Constants
    11. 6.11. Custom Allocators
  7. 7. Glossary
  8. 8. Syntax Index
  9. 9. Bibliography

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, let’s check out some generic code. Rust’s standard library provides a type, Option<T>, that’s generic:

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

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:

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

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 that’s an i32, the two sides match, and Rust is happy. If they didn’t match, we’d 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)Run

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

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

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

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

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:

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

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:

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

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) {
    // ...
}Run

We could write a version that takes multiple types:

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

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 };Run

Similar 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 declare the type parameter after the impl:

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

So far you’ve seen generics that take absolutely any type. These are useful in many cases: you’ve already seen Option<T>, and later you’ll 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.