Generics
Generic Functions
Section titled “Generic Functions”Instead of writing largest_i32, largest_f64, and largest_char as separate functions, generics let you write one:
fn largest<T: PartialOrd>(list: &[T]) -> &T { let mut biggest = &list[0]; for item in list { if item > biggest { biggest = item; } } biggest}The <T: PartialOrd> part declares a type parameter T with a trait bound. The bound PartialOrd means “this function accepts any type that supports the > and < operators.” Without the bound, the compiler would refuse the comparison item > biggest because not every type is comparable.
Generic Structs
Section titled “Generic Structs”Structs can also be parameterized. A Pair<T> holds two values of the same type T:
struct Pair<T> { first: T, second: T,}When you instantiate it, the compiler infers T from the values you provide:
let p = Pair { first: 5, second: 10 }; // T inferred as i32impl Blocks for Generic Structs
Section titled “impl Blocks for Generic Structs”To add methods to a generic struct, repeat the type parameter in the impl line:
impl<T: std::fmt::Display + PartialOrd> Pair<T> { fn larger(&self) -> &T { if self.first > self.second { &self.first } else { &self.second } }}Here T must implement both Display (for printing) and PartialOrd (for comparison). You can constrain methods further than the struct itself requires — a Pair<T> can exist for any T, but larger is only available when T is comparable.
Trait Bounds and where Clauses
Section titled “Trait Bounds and where Clauses”Inline bounds work well for one or two constraints. When a signature grows long, a where clause keeps it readable:
// Inline boundsfn notify<T: Clone + std::fmt::Debug>(item: T) { ... }
// Equivalent where clausefn notify<T>(item: T)where T: Clone + std::fmt::Debug,{ ... }Both forms are identical to the compiler; choose whichever is clearer.
Multiple Type Parameters
Section titled “Multiple Type Parameters”You are not limited to one parameter. A function can accept two or more:
fn zip_display<A: std::fmt::Display, B: std::fmt::Display>(a: A, b: B) { println!("{} + {}", a, b);}Try It
Section titled “Try It”The playground demonstrates a generic largest function working on both integers and characters, plus a generic Pair<T> struct with a method constrained to comparable, displayable types.
fn largest<T: PartialOrd>(list: &[T]) -> &T { let mut biggest = &list[0]; for item in list { if item > biggest { biggest = item; } } biggest}
struct Pair<T> { first: T, second: T,}
impl<T: std::fmt::Display + PartialOrd> Pair<T> { fn new(first: T, second: T) -> Self { Self { first, second } }
fn larger(&self) -> &T { if self.first > self.second { &self.first } else { &self.second } }}
fn main() { let numbers = vec![34, 50, 25, 100, 65]; println!("largest number: {}", largest(&numbers));
let chars = vec!['y', 'm', 'a', 'q']; println!("largest char: {}", largest(&chars));
let p = Pair::new(5, 10); println!("larger of pair: {}", p.larger());}Compiling…