In this lesson, you will learn how to use trait bounds to force incoming data to satisfy strict business logic requirements before it ever touches your struct’s internal state.
The Problem: Weak Setters
In many applications, we store data in structs that have specific "invariants"—rules that must always be true, such as a percentage being between 0 and 100, or a username containing no special characters. A standard setter method often accepts any type, leaving you to handle error checking inside the struct.
The Solution: Trait-Bound Setters
Instead of accepting raw data, we can define a trait that acts as a "Gatekeeper." We force our setter to only accept types that implement this trait, effectively moving the validation logic outside the struct and onto the data provider.
Imagine we have a Percentage struct that must always be between 0 and 100. We can define a trait ValidatedValue that requires a check method:
trait ValidatedValue {
fn is_valid(&self) -> bool;
}
struct Percentage(u8);
impl Percentage {
// Only accept types that prove they are valid via the trait
fn set_value<T: ValidatedValue>(&mut self, value: T) -> Result<(), &'static str> {
if value.is_valid() {
// Logic to update...
Ok(())
} else {
Err("Invalid data provided")
}
}
}
Why This Works
By requiring the T: ValidatedValue trait bound, you create a contractual requirement. Any code trying to update your Percentage struct cannot even call the method unless it provides an object that has already passed the validation logic defined in the trait.
This architecture decouples the "check" from the "store." Your struct doesn't need to know how validation happens; it only cares that the incoming data has signed the contract (via the trait) guaranteeing its integrity. This prevents invalid states from ever reaching your core data structures, keeping your memory footprint safe and your state predictable.
Key Takeaway: By using trait-bound setters, you shift the burden of validation from the struct’s internal logic to a required contract, ensuring that only verified data can trigger state changes.