API Design Patterns
Trait API
Depend on Behavior
A function can accept a trait reference so callers provide behavior without exposing concrete types.
Program
Play the program to choose a status code and format it through a trait boundary.
trait_formatter_boundary.rs
trait Formatter {
fn status(&self, code: u16) -> String;
}
struct PlainFormatter;
impl Formatter for PlainFormatter {
fn status(&self, code: u16) -> String {
format!("status={code}")
}
}
fn render(formatter: &dyn Formatter, code: u16) -> String {
formatter.status(code)
}
fn main() {
let code = ;
let formatter = PlainFormatter;
let text = render(&formatter, code);
println!("{text}");
}
trait Formatter {
fn status(&self, code: u16) -> String;
}
struct PlainFormatter;
impl Formatter for PlainFormatter {
fn status(&self, code: u16) -> String {
format!("status={code}")
}
}
fn render(formatter: &dyn Formatter, code: u16) -> String {
formatter.status(code)
}
fn main() {
let code = ;
let formatter = PlainFormatter;
let text = render(&formatter, code);
println!("{text}");
}
trait Formatter {
fn status(&self, code: u16) -> String;
}
struct PlainFormatter;
impl Formatter for PlainFormatter {
fn status(&self, code: u16) -> String {
format!("status={code}")
}
}
fn render(formatter: &dyn Formatter, code: u16) -> String {
formatter.status(code)
}
fn main() {
let code = ;
let formatter = PlainFormatter;
let text = render(&formatter, code);
println!("{text}");
}
trait
`Formatter` describes behavior without naming a concrete implementation.
trait object
`&dyn Formatter` accepts any formatter behind a shared reference.
boundary
`render` depends on formatting behavior, not on the `PlainFormatter` type.