Multiple Bounds
Part of the Object Oriented Programming section of Coddy's Rust journey — lesson 41 of 61.
Sometimes a single trait bound isn't enough. You might need a generic type that can both be printed and provide a summary. Rust lets you require multiple traits using the + syntax.
Here's how to specify that a type must implement two traits:
use std::fmt::Display;
trait Summary {
fn summarize(&self) -> String;
}
fn announce<T: Display + Summary>(item: T) {
println!("Breaking news: {}", item);
println!("Summary: {}", item.summarize());
}
The bound T: Display + Summary means "T must implement both Display and Summary." Inside the function, you can use capabilities from both traits—printing with {} (from Display) and calling summarize() (from Summary).
You can chain as many traits as needed:
fn process<T: Display + Summary + Clone>(item: T) {
// Can print, summarize, AND clone
}
This pattern is essential when your function relies on multiple behaviors. Rather than accepting any type and hoping it works, you explicitly declare exactly what capabilities are required—and the compiler enforces it at compile time.
Challenge
EasyLet's build a product inspection system that requires items to have multiple capabilities! You'll create a generic function that only accepts types implementing both a custom trait and a standard trait, demonstrating how the + syntax combines multiple bounds.
You'll organize your code across two files:
product.rs: Define a publicInspectabletrait with a method calledinspectthat takes&selfand returns aStringcontaining inspection details. Then create a publicGadgetstruct with publicname(String) andserial(u32) fields. Your Gadget should implement bothInspectable(returningInspecting: {name}) andstd::fmt::Display(formatting as{name} (SN: {serial})). Finally, create a public generic function calledfull_reportthat accepts any typeTimplementing bothDisplayandInspectable. This function should print two lines: first the item using the{}formatter, then the result of callinginspect().main.rs: Bring in your product module and create aGadgetinstance using the inputs provided. Callfull_reportwith your gadget to show that it satisfies both trait requirements.
The power of multiple bounds is that your full_report function can use capabilities from both traits—displaying the item nicely AND getting inspection details—all guaranteed at compile time.
Your output should show both the display format and the inspection result:
{name} (SN: {serial})
Inspecting: {name}For example, with inputs Smartwatch and 98765:
Smartwatch (SN: 98765)
Inspecting: SmartwatchYou will receive two inputs: the gadget name and the serial number (parse as u32).
Cheat sheet
You can require multiple trait bounds on a generic type using the + syntax:
fn function_name<T: Trait1 + Trait2>(item: T) {
// Can use capabilities from both traits
}
Example with Display and a custom trait:
use std::fmt::Display;
trait Summary {
fn summarize(&self) -> String;
}
fn announce<T: Display + Summary>(item: T) {
println!("Breaking news: {}", item);
println!("Summary: {}", item.summarize());
}
You can chain multiple traits as needed:
fn process<T: Display + Summary + Clone>(item: T) {
// Can print, summarize, AND clone
}
The compiler enforces that the type implements all specified traits at compile time.
Try it yourself
mod product;
use product::{Gadget, full_report};
fn main() {
// Read inputs
let mut name = String::new();
std::io::stdin().read_line(&mut name).expect("Failed to read line");
let name = name.trim().to_string();
let mut serial_input = String::new();
std::io::stdin().read_line(&mut serial_input).expect("Failed to read line");
let serial: u32 = serial_input.trim().parse().expect("Failed to parse serial");
// TODO: Create a Gadget instance with the name and serial
// TODO: Call full_report with your gadget
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Methods and Behavior
Intro Implementation BlocksThe Self ParameterMutable MethodsAssociated FunctionsMultiple Implementation BlocksMethod ChainingRecap - Rectangle Actions4Project: Virtual Pet
Defining the PetFeeding the Pet7Standard Traits
The Debug TraitThe Display TraitClone and CopyEquality TraitsRecap - Printable Point10Project: Document System
The Draw TraitText Component2Encapsulation and Modules
Modules BasicsThe Public KeywordPrivate FieldsGettersSettersRecap - Secure Locker5Generics
Generic StructsGeneric MethodsMultiple Generic TypesGeneric FunctionsRecap - Coordinate Point8Traits as Bounds
Trait Bounds SyntaxMultiple BoundsThe Where ClauseReturning Types with TraitsRecap - Generic Printer11Design Patterns in Rust
Newtype PatternCompositionThe Drop TraitFrom and IntoRecap - Smart Pointer Mock