Skip to content

Latest commit

 

History

History
126 lines (91 loc) · 2.33 KB

10_generics.md

File metadata and controls

126 lines (91 loc) · 2.33 KB

Generics

Generics allow specifying placeholders for concrete types:

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

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

The compiler substitutes these placeholders for concrete types at compile-time.

Turbofish

Specifying type parameters explicitly on generic types is done with the turbofish syntax ::<>:

fn main() {
    let v = (1..10).collect::<Vec<u32>>();
}

This is useful in case the compiler cannot infer the type, or you want to change the inferred type to something different.

Defaults

The type can be defaulted using the T = Default syntax:

struct Point<T = u64>(T, T);

fn main() {
    let p = Point(13, 37);        // u64
    let p = Point::<i32>(4, 20);  // i32
}

Constants

The so-called const generics allow using constant values as generic parameters:

struct Matrix<const S: usize>([[i32; S]; S]);

type Matrix3D = Matrix<3>;

fn main() {
    let m: Matrix3D = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
}

They're defined using the const T: type syntax.

Associated type

Associated types are a type of generics whose purpose is to simplify code management.

Code using the associated type can be replaced with code using the generic type, but not the other way around.

Associated type is specified using type in the impl block and can be accessed with :::

trait Graph {
    type N;
    type E;
    fn has_edge(&self, start: &N, end: &N) -> bool;
}

fn distance<G: Graph>(graph: &G, start: &G::N, end: &G::N) -> uint {
    // ...
}

The same defined using generics is a lot less readable:

trait Graph<N, E> {
    fn has_edge(&self, start: &N, end: &N) -> bool;
}

fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> uint {
    // ...
}

Associated types can be defaulted, allowing both flexibility and clean syntax:

trait Add<Rhs = Self> {
    type Output = Rhs;
    fn add(&self, rhs: Rhs) -> Self::Output;
}

struct Meters(u32);

struct Millimeters(u32);

impl Add for Meters {
    type Output = Meters;

    fn add(self, rhs: Meters) -> Meters {
        Meters(self.0 + rhs.0)
    }
}

impl Add<Meters> for Millimeters {
    type Output = Millimeters;

    fn add(self, rhs: Meters) -> Millimeters {
        Millimeters(self.0 + (rhs.0 * 1000))
    }
}