RustArena

Step 1 of 6

Generic functions

Say you need the last element of a list of scores, then of a list of names, then of a list of prices. Without generics you'd write three functions with identical bodies:

fn last_i32(items: &[i32]) -> Option<&i32> { ... }
fn last_str(items: &[&str]) -> Option<&&str> { ... }
fn last_f64(items: &[f64]) -> Option<&f64> { ... }

Generics let you write it once, with a placeholder for the type:

fn last<T>(items: &[T]) -> Option<&T> {
    if items.is_empty() {
        None
    } else {
        Some(&items[items.len() - 1])
    }
}

fn swap<A, B>(pair: (A, B)) -> (B, A) {
    (pair.1, pair.0)
}

fn main() {
    let scores = [88, 92, 75];
    let names = vec!["ana", "bo"];
    let empty: [f64; 0] = [];
    println!("{:?}", last(&scores));
    println!("{:?}", last(&names));
    println!("{:?}", last(&empty));
    println!("{:?}", swap(("crab", 8)));
}
Some(75)
Some("bo")
None
(8, "crab")

Piece by piece:

You've been using generic code all along: Vec<T>, Option<T>, Result<T, E> and HashMap<K, V> are generic types, and slice methods like .last() and .first() are generic functions.

Naming: type parameters are usually single capital letters: T for "type", U for a second one, K/V for keys and values, E for errors. A descriptive name like Item is fine when it reads better.

Common mistakes

fn last(items: &[T]) -> Option<&T> {
    items.last()
}

fn main() {
    println!("{:?}", last(&[1, 2]));
}
error[E0425]: cannot find type `T` in this scope
  | fn last(items: &[T]) -> Option<&T> {
  |                  ^ not found in this scope
  help: you might be missing a type parameter

Your task

Fill in the blanks so middle works for slices of any type. It returns the element in the middle (at index len / 2), or None for an empty slice. The program prints:

Some(3)
Some("green")
Some('y')
None

Challenge 2

Write fn last_two<T>(items: &[T]) -> Option<(&T, &T)> that returns references to the last two elements, in order, or None if there are fewer than two. For [1, 2, 3] it returns Some((&2, &3)).

Challenge 3

Write a generic function from scratch: reverse_in_place, which takes a mutable slice of any type and reverses it in place. Use items.swap(i, j), which swaps two elements of a slice, and don't call .reverse(). It must work for numbers and for Strings, and do nothing for an empty slice.

Next: Trait bounds: what T is allowed to do