Practical Crates
Dependency Adapter
Program to a Trait
A crate boundary is easier to test when application code depends on a trait instead of a concrete external client.
Program
Play the program to choose a retry count and see the client wrapped behind a small trait.
dependency_adapter.rs
fn main() {
let retries = ;
let client = MockClient { status: "ok" };
let message = fetch_status(&client, retries);
println!("{message}");
}
trait StatusClient {
fn status(&self) -> &'static str;
}
struct MockClient {
status: &'static str,
}
impl StatusClient for MockClient {
fn status(&self) -> &'static str {
self.status
}
}
fn fetch_status(client: &impl StatusClient, retries: i32) -> String {
format!("{} after {} retries", client.status(), retries)
}
fn main() {
let retries = ;
let client = MockClient { status: "ok" };
let message = fetch_status(&client, retries);
println!("{message}");
}
trait StatusClient {
fn status(&self) -> &'static str;
}
struct MockClient {
status: &'static str,
}
impl StatusClient for MockClient {
fn status(&self) -> &'static str {
self.status
}
}
fn fetch_status(client: &impl StatusClient, retries: i32) -> String {
format!("{} after {} retries", client.status(), retries)
}
fn main() {
let retries = ;
let client = MockClient { status: "ok" };
let message = fetch_status(&client, retries);
println!("{message}");
}
trait StatusClient {
fn status(&self) -> &'static str;
}
struct MockClient {
status: &'static str,
}
impl StatusClient for MockClient {
fn status(&self) -> &'static str {
self.status
}
}
fn fetch_status(client: &impl StatusClient, retries: i32) -> String {
format!("{} after {} retries", client.status(), retries)
}
adapter
`StatusClient` is the stable boundary that application code depends on.
mock
`MockClient` lets the example run without a network client or third-party crate.
dependency seam
`fetch_status` can accept another client later if it implements the same trait.