In this lesson, you will learn how to demand that a trait object satisfies several different interface requirements simultaneously using the + syntax.
Combining Traits
Sometimes, your function needs a value that behaves like multiple things at once. For example, you might need an object that is both Display (so it can be printed) and Debug (so it can be inspected for errors).
When working with generics, we use the T: TraitA + TraitB syntax. When working with trait objects—which involve dynamic dispatch via dyn—we apply that same syntax directly to the type. This creates a single "fat pointer" that knows how to access the methods of both traits.
How to use dyn TraitA + TraitB
Consider a scenario where you are processing data that must be both readable and clonable. You can define a parameter as &dyn std::fmt::Display + Clone.
fn process_item(item: &(dyn std::fmt::Display + Clone)) {
// We can print it because it satisfies Display
println!("Processing: {}", item);
// We can clone it because it satisfies Clone
let _copy = item.clone();
}
The Rule of One Concrete Type
There is one important restriction: you can only list one "base" trait that is not an auto trait (like Send or Sync). If you try to combine two custom traits, like dyn Readable + Writeable, the compiler will usually ask you to create a "supertrait" instead:
trait DataHandler: Readable + Writeable {}
// Now use: &dyn DataHandler
This requirement exists because the Rust compiler needs a single vtable pointer layout to determine how to dispatch calls. By bundling them into a supertrait, you provide a clear contract for the memory layout of the trait object. If you are only combining a standard trait with an auto trait (like dyn Display + Send), the compiler is perfectly happy to manage that for you.
Key Takeaway: You can use the + operator within a dyn type to enforce that a trait object implements multiple interfaces, ensuring the underlying value provides every method you need.