← Back to Rust Course | Chapter 11: Generics & Traits | Lesson 1 of 7

Generic Functions

A generic function is written once but can work with many different types of values, like a universal tool.

Defining a Generic Function

A generic type parameter, declared in angle brackets before the parameter list, lets a single function definition work across many different concrete types.

Example: Defining a Generic Function

markup
fn identity<T>(value: T) -> T {
    value
}

fn main() {
    println!("{}", identity(5));
    println!("{}", identity("hello"));
}

Generic Function with a Trait Bound

To perform operations like comparison inside a generic function, you must add a trait bound telling the compiler which types are actually allowed, such as T: PartialOrd.

Example: Generic Function with a Trait Bound

markup
fn largest<T: PartialOrd>(a: T, b: T) -> T {
    if a > b { a } else { b }
}

fn main() {
    println!("{}", largest(3, 7));
    println!("{}", largest(2.5, 1.1));
}

Generics Over Multiple Types

A function can have several independent generic type parameters, each potentially constrained by different trait bounds, letting it flexibly combine unrelated types.

Example: Generics Over Multiple Types

markup
fn make_pair<A, B>(first: A, second: B) -> (A, B) {
    (first, second)
}

fn main() {
    let pair = make_pair(1, "one");
    println!("{:?}", pair);
}

Zero-Cost Abstraction

The Rust compiler generates a specialized version of a generic function for each concrete type it is used with (called monomorphization), so calling a generic function has no runtime overhead compared to writing separate functions by hand.

Example: Zero-Cost Abstraction

markup
fn double<T: std::ops::Add<Output = T> + Copy>(value: T) -> T {
    value + value
}

fn main() {
    println!("{}", double(21));
    println!("{}", double(1.5));
}
Common Mistakes
  1. Forgetting to declare the generic type parameter (<T>) before using it in the function signature.
  2. Assuming a generic function can perform any operation on T -- without trait bounds, only operations valid for every possible type are allowed.
  3. Writing a separate near-identical function for each concrete type instead of factoring out a single generic version.
Chapter Summary
  • Generic type parameters, written <T>, let a function operate over many concrete types with one definition.
  • Without trait bounds, the compiler only allows operations that are valid for absolutely any type.
  • Trait bounds like T: PartialOrd restrict T to types that support specific operations, such as comparison.
  • The compiler generates specialized code for each concrete type used, so generics have no runtime performance cost.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.