kota's memex

Functions in rust can take parameters of a generic type rather than a concrete type like i32 or String. Generics allow us to write a function that operates on promises made about a type; without complete information of the type.

functions

fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

Without generics we would need to write multiple of these functions for each type we would like to support:

fn largest_i32(list: &[i32]) -> &i32 {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn largest_char(list: &[char]) -> &char {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

However, we can use generics to instead promise that our list is ordered, but not what kind of data it is. Since our function doesn't need to know what type of data it is this works and we can avoid writing duplicate functions. We make that promise with std::cmp::PartialOrd which is a rust trait.

structs

Structs can also have generic properties:

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

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

fn main() {
    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };
}

It's worth noting that when we select a concrete type for T we've "looked it in" for the context of that instance:

let wont_work = Point { x: 5, y: 4.0 }; // Compiler error!

methods with constraints

We can create methods for generic types which only work with certain concrete types:

impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

enums

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