Generics

From Rust Community Wiki
Jump to navigation Jump to search

Generics are a concept to refer to many types at once, and to be polymorphic over them. Generics use the angle bracket syntax (<T>), which is common in many C-like languages.

An identifier that refers to many types is called a type parameter, a type variable or simply a generic type. A type, trait, function or impl block with a type parameter is called generic. Strictly speaking, types, traits, functions and impl blocks with a lifetime are also generic. For functions, this isn't always visible, since lifetimes are often elided.

Example[edit | edit source]

This example illustrates where generics can be used. The identifier T is used as type parameter, which is common practice in Rust.

// generic trait
trait Foo<T> {}

// generic types
struct MyStruct<T>(T);
enum MyEnum<T> { Value(T) }
union MyUnion<T> { field: T };
type MyType<T> = MyEnum<MyUnion<T>>;

// generic implementations
impl<T> MyStruct<T> {}
impl<T> Foo<T> for (T, T) {}

// generic function
fn my_function<T: Foo>(foo: T) {}

Each type parameter must be declared before it can be used:

struct MyStruct<T> { // declaration of T
    value: T,        // usage of T
}

impl<T> MyTrait for MyStruct<T> {}
//   ^~ declaration of T     ^~ usage of T

trait MyTrait {
    fn my<T>(value: MyStruct<T>);
    //    ^~ declaration     ^~ usage
}

Bounds[edit | edit source]

Type parameters and lifetimes can be bounded by traits and by lifetimes. Bounds appear after a colon (:) after the parameter and are separated with a + sign. It is possible to move bounds to a where clause:

fn foo<'a, T: SomeTrait + OtherTrait + 'a>(value: &'a T) {}

struct Foo<T: Clone + ?Sized>(T);

// this can be written as
fn foo<'a, T>(value: &'a T)
where
    T: SomeTrait + OtherTrait + 'a
{}

struct Foo<T>(T)
where
    T: Clone + ?Sized;

Lifetimes on generics[edit | edit source]

Each type parameter has an implicit lifetime bound, since it can potentially be used for a type which borrows data. This can be prevented by adding an explicit 'static bound, which means that the type must not borrow values, unless these borrows have the 'static lifetime:

struct Foo<T: 'static>(T);

let f1 = Foo(5);   // works, since nothing is borrowed
let f2 = Foo("hi") // works, since string literals are 'static
let f3 = Foo(&f1); // ERROR! &f1 doesn't have the 'static lifetime

When omitting the lifetime, it is usually inferred correctly, so this doesn't need to be considered except in rare edge cases.

Usage[edit | edit source]

When using a generic trait or type, its type parameters must be substituted with concrete types or with new type parameters.

TODO