Exerpad
🔥 1
10 XP
Lesson · Chapter: Generics+10 XP for reading

Generic Code

What if you want a function that works with i32 AND f64 AND String? Instead of writing separate functions for each type, you can write one using generics!

The Problem

Without generics, you'd need duplicate functions:

rustexample — not runnable
fn print_i32(value: i32) {
println!("Value: {}", value);
}

fn print_f64(value: f64) {
println!("Value: {}", value);
}

That's a lot of repetition!

Generic Functions

A generic function uses a type parameter (usually T) that stands for "any type":

rust
fn print_value<T: std::fmt::Display>(value: T) {
println!("Value: {}", value);
}

fn main() {
print_value(42);
print_value(3.14);
print_value("hello");
}
Output
Press Run to see the output.

The <T: std::fmt::Display> means "T can be any type, as long as it can be printed with {}".

Generic Structs

Structs can also use generics:

rust
#[derive(Debug)]
struct Wrapper<T> {
value: T,
}

fn main() {
let int_wrap = Wrapper { value: 42 };
let str_wrap = Wrapper { value: String::from("hello") };
println!("{:?}", int_wrap);
println!("{:?}", str_wrap);
}
Output
Press Run to see the output.

One Wrapper struct works with any type!

Multiple Type Parameters

You can have more than one generic type:

rust
#[derive(Debug)]
struct Pair<A, B> {
first: A,
second: B,
}

fn main() {
let p = Pair { first: "age", second: 10 };
println!("{:?}", p);
}
Output
Press Run to see the output.

Trait Bounds

When you need your generic type to have certain abilities, use trait bounds:

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

fn main() {
println!("Larger: {}", largest(10, 20));
println!("Larger: {}", largest(3.5, 1.2));
}
Output
Press Run to see the output.

T: PartialOrd means "T must support comparison operators like >=".

Multiple Trait Bounds

Use + to require multiple traits:

rust
fn show_largest<T: PartialOrd + std::fmt::Display>(a: T, b: T) {
if a >= b {
println!("Largest: {}", a);
} else {
println!("Largest: {}", b);
}
}

fn main() {
show_largest(10, 20);
show_largest("apple", "banana");
}
Output
Press Run to see the output.

Generic Methods

You can add methods to generic structs:

rust
#[derive(Debug)]
struct Container<T> {
item: T,
}

impl<T: std::fmt::Display> Container<T> {
fn show(&self) {
println!("Contains: {}", self.item);
}
}

fn main() {
let c = Container { item: 42 };
c.show();
}
Output
Press Run to see the output.

Where Clauses

For complex bounds, use where for cleaner code:

rust
fn compare_and_show<T>(a: T, b: T)
where
T: PartialOrd + std::fmt::Display,
{
if a >= b {
println!("{} wins!", a);
} else {
println!("{} wins!", b);
}
}

fn main() {
compare_and_show(10, 20);
}
Output
Press Run to see the output.

Summary

ConceptWhat it does
fn name<T>(x: T)Generic function with type parameter
struct Name<T> { field: T }Generic struct
T: DisplayTrait bound — T must implement Display
T: PartialOrd + DisplayMultiple trait bounds
where T: DisplayWhere clause for cleaner bounds
impl<T> Name<T> { ... }Methods on generic struct

Now let's practice writing generic code!