In this lesson, you will learn how to restrict your generic functions to only accept types that possess specific capabilities.
The Problem with "Too Generic"
When you write a generic function like fn print_it<T>(item: T), Rust is very cautious. Because T could be literally anything—a number, a struct, or a complex pointer—Rust doesn’t assume it can do anything special. For example, if you try to call println!("{}", item);, the compiler will complain. It doesn't know if T knows how to be formatted as a string.
Adding Constraints with Trait Bounds
To fix this, we use Trait Bounds. A trait bound tells the compiler: "I am writing a generic function, but I only want to accept types that implement a specific trait."
Think of it like a job posting: you aren't just looking for any human; you are looking for a human who has the trait of knowing how to code.
You declare these bounds by placing a colon after the generic type parameter, followed by the name of the trait you require:
use std::fmt::Display;
fn print_it<T: Display>(item: T) {
println!("The value is: {}", item);
}
In this example, <T: Display> is the trait bound. It promises the compiler that item is guaranteed to have the functionality defined in the Display trait. Because Display is required for the {} format specifier, the code now compiles perfectly.
Why this matters
Trait bounds are how Rust balances flexibility with safety. You keep the power of writing code that works for many different types, but you gain the compiler's help in ensuring that the types passed in actually support the operations you are performing. Without these bounds, the compiler would have to assume the worst, and you wouldn't be able to call any methods on your generic variables at all.
By restricting what T can be, you actually unlock the ability to do more with your generic code.
Key Takeaway: Trait bounds allow you to specify that a generic type must implement a particular trait, ensuring the type has the capabilities required by your function logic.