Pybites Logo Rust Platform

Generic Functions

Medium +3 pts

🎯 In Python, functions work with any type by default — duck typing:

def middle(items):
    if not items:
        return None
    return items[len(items) // 2]

middle([10, 20, 30])          # 20
middle(["a", "b", "c", "d"]) # "b"

No type annotations needed. If it has __getitem__, it works. If it doesn't — runtime error.

Rust's approach is different: generic functions work with many types, but the compiler verifies at compile time that each type supports the operations you use. The <T> syntax declares a type parameter:

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

This works with &[i32], &[String], &[bool] — any slice type. The compiler generates optimized code for each concrete type you use. Zero runtime cost, full type safety.

When T isn't enough: trait bounds

Some functions need types that support specific operations. A find_min function needs to compare values. In Python, min() works on anything that supports < — and you find out at runtime if it doesn't.

In Rust, you declare what T must support using trait bounds:

fn find_min<T: Ord>(items: &[T]) -> Option<&T> {
    items.iter().min()
}

T: Ord means "T must implement the Ord trait" (total ordering). The compiler checks this at compile time — if you try find_min on a type that isn't Ord, you get a compile error, not a runtime crash.

Common trait bounds:

BoundWhat it requiresPython equivalent
T: OrdTotal ordering (<, >, ==)__lt__ + __eq__
T: PartialEqEquality comparison (==)__eq__
T: CloneCan be duplicatedcopy.copy()
T: DisplayUser-facing formatting__str__
T: DebugDeveloper formatting__repr__

Multiple bounds with +

When a function needs multiple capabilities:

fn print_and_clone<T: Display + Clone>(item: &T) -> T {
    println!("{}", item);
    item.clone()
}

T: Display + Clone means T must implement both traits. Python achieves this through duck typing — if the object has both __str__ and supports copy, it works. Rust requires you to declare the requirements explicitly.

Slices: the generic view

Notice that the functions take &[T] — a slice reference. This works with vectors, arrays, and other contiguous collections:

let vec = vec![1, 2, 3];
let arr = [1, 2, 3];

middle(&vec);    // works — Vec<T> coerces to &[T]
middle(&arr);    // works — [T; N] coerces to &[T]

This is like Python functions accepting any sequence — but with compile-time guarantees about what operations are available.

Login to see the full task and start coding.

This is a premium exercise

Log in to unlock the full exercise and start coding.

Login to access this exercise