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
Replay: real traced execution (multi-file project)
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 = 200;
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 = 404;
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 = 500;
let formatter = PlainFormatter;
let text = render(&formatter, code);
println!("{text}");
}
code ← 200, formatter ← (empty)
17fn main() {18 let cod→ 200e = 200; //@code=200, 404, 50019 let formatte→ (empty)r = PlainFormatter;20 let text = render(&formatte(empty)r, cod200e);21 println!("{text}");text ← "status=200"
19 let formatter = PlainFormatter;20 let tex→ "status=200"t = render(&formatte(empty)r, cod200e);21 println!("{text}");22}outputstatus=200
code ← 404, formatter ← (empty)
17fn main() {18 let cod→ 404e = 404;19 let formatte→ (empty)r = PlainFormatter;20 let text = render(&formatte(empty)r, cod404e);21 println!("{text}");text ← "status=404"
19 let formatter = PlainFormatter;20 let tex→ "status=404"t = render(&formatte(empty)r, cod404e);21 println!("{text}");22}outputstatus=404
code ← 500, formatter ← (empty)
17fn main() {18 let cod→ 500e = 500;19 let formatte→ (empty)r = PlainFormatter;20 let text = render(&formatte(empty)r, cod500e);21 println!("{text}");text ← "status=500"
19 let formatter = PlainFormatter;20 let tex→ "status=500"t = render(&formatte(empty)r, cod500e);21 println!("{text}");22}outputstatus=500
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.