Exerpad
🔥1
10 XP
Lv.1 Beginner
Log In

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:

rust
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");
}

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);
}

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);
}

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));
}

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");
}

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();
}

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);
}

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!