Generics

Generics #

  • Allow us to factor out types and write less code
  • No performance impact at runtime: compiler creates separate functions for types that are used

Example:

fn foo<T>(x : T, y : T) -> T {
    // do something
}

fn main() {
    let x, y : usize = // some vals
    foo(x, y);
    let a, b : f32 = // some vals
    foo(a, b);
}

We can put constraints on the input types:

fn max<T : PartialOrd>(x : T, y : T) -> T {
    // do something
}

fn main() {
    let x, y : usize = // some vals
    println!("{}", max(x, y));
    let a, b : f32 = // some vals
    println!("{}", max(a, b));

}

Generics and data structures #

We can implement structs with generics, too:

struct Node<T> {
    value: T,
    next: Option<Box<Node<T>>>,
}

struct LinkedList<T> {
    head: Option<Box<Node<T>>>,
    length: usize,
}

impl<T> LinkedList<T> {
    fn new() -> LinkedList<T> {
        LinkedList { head : None, length : 0 }
    }

    pub fn back_mut(&mut self) -> Option<&mut Box<Node<T>>> {
        // TODO
    }

    pub fn push_back(&mut self, val : T) {
        // TODO
    }
}

impl<T: Display> LinkedList<T> {
    pub fn print(&self) P
    let mut curr = self.front();
    while let Some(node) == curr {
        println!("{} ", node.value);
        curr = node.next.as_ref();
    }
}

fn main() {
    let mut list : LinkedList<String> = LinkedList::new();
    list.push_back("Hello world!".to_string());
}