Generic Functions
🎯 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:
| Bound | What it requires | Python equivalent |
|---|---|---|
T: Ord | Total ordering (<, >, ==) | __lt__ + __eq__ |
T: PartialEq | Equality comparison (==) | __eq__ |
T: Clone | Can be duplicated | copy.copy() |
T: Display | User-facing formatting | __str__ |
T: Debug | Developer 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