a79249bbff
- traits.md: iterator.rs anchor L76→L42 (pub const trait Iterator) - traits.md: deref.rs anchor L60→L139 (pub const trait Deref) - error-handling.md: fix variant names CliError::Io→IoError, Parse→ParseError to match actual stdlib doc example at convert/mod.rs:557 - concurrency.md: mutex.rs and rwlock.rs moved to sync/poison/ subtree (3 link updates: mutex×2, rwlock×1)
700 lines
19 KiB
Markdown
700 lines
19 KiB
Markdown
# Rust Trait Patterns
|
|
|
|
Patterns for trait design in Rust, extracted from the standard
|
|
library source at rust-lang/rust.
|
|
|
|
**Source:** [rust-lang/rust](https://github.com/rust-lang/rust) at commit
|
|
[`f53b654`](https://github.com/rust-lang/rust/tree/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6)
|
|
|
|
**Stats:** 241 public traits, 4,387 trait implementations, 508 From
|
|
impls, 895 Iterator impls, 269 Display impls, 226 Default impls.
|
|
|
|
---
|
|
|
|
## 1. Small Traits (One Required Method)
|
|
|
|
### Source:
|
|
|
|
[library/core/src/iter/traits/iterator.rs#L42](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/iter/traits/iterator.rs#L42)
|
|
|
|
```rust
|
|
// library/core/src/iter/traits/iterator.rs
|
|
pub trait Iterator {
|
|
type Item;
|
|
|
|
fn next(&mut self) -> Option<Self::Item>;
|
|
|
|
// 70+ provided methods built on top of `next()`...
|
|
fn map<B, F>(self, f: F) -> Map<Self, F> { ... }
|
|
fn filter<P>(self, predicate: P) -> Filter<Self, P> { ... }
|
|
fn collect<B>(self) -> B where B: FromIterator<Self::Item> { ... }
|
|
// ...
|
|
}
|
|
```
|
|
|
|
Iterator has 1 required method (`next`) and 70+ provided methods.
|
|
Implementing Iterator for your type requires writing ONE function.
|
|
|
|
### Why
|
|
|
|
Minimize the cost of implementing the trait while maximizing the
|
|
value. One required method means trivial to implement; provided
|
|
methods give the full API for free. 895 Iterator implementations
|
|
exist in the stdlib because it's so easy.
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- You want many types to implement your trait
|
|
- There's a clear "minimal kernel" that all implementations share
|
|
- Other methods can be defined in terms of that kernel
|
|
|
|
**Example — before:**
|
|
```rust
|
|
// Fat trait — every implementor must write 5 methods
|
|
trait Sequence {
|
|
fn next(&mut self) -> Option<Self::Item>;
|
|
fn size_hint(&self) -> (usize, Option<usize>);
|
|
fn count(self) -> usize;
|
|
fn last(self) -> Option<Self::Item>;
|
|
fn nth(&mut self, n: usize) -> Option<Self::Item>;
|
|
}
|
|
// Result: few types implement it because it's too much work
|
|
```
|
|
|
|
**Example — after:**
|
|
```rust
|
|
// Minimal required, rest provided
|
|
trait Sequence {
|
|
type Item;
|
|
fn next(&mut self) -> Option<Self::Item>; // ONE required method
|
|
|
|
// Provided defaults — free for all implementors:
|
|
fn size_hint(&self) -> (usize, Option<usize>) { (0, None) }
|
|
fn count(self) -> usize { self.fold(0, |c, _| c + 1) }
|
|
fn last(self) -> Option<Self::Item> { self.fold(None, |_, x| Some(x)) }
|
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
|
for _ in 0..n { self.next(); }
|
|
self.next()
|
|
}
|
|
}
|
|
```
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- Methods are genuinely independent (can't be derived from each other)
|
|
- Implementors SHOULD be forced to think about each method
|
|
(e.g., `PartialOrd` requires `partial_cmp` because you can't
|
|
derive comparison from equality)
|
|
- The "default" would be wrong for most types
|
|
|
|
---
|
|
|
|
## 2. Derive Macros for Standard Traits
|
|
|
|
### Source:
|
|
|
|
1,116 `#[derive(...)]` annotations in library/. Top derives:
|
|
Clone (880), Debug (869), Copy (537), PartialEq (388), Eq (285).
|
|
|
|
```rust
|
|
// Typical stdlib type:
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct Duration {
|
|
secs: u64,
|
|
nanos: Nanoseconds,
|
|
}
|
|
```
|
|
|
|
### Why
|
|
|
|
Derive generates correct implementations mechanically. Hand-writing
|
|
Clone/Debug/PartialEq is error-prone and repetitive. The derive
|
|
order is a convention: Debug first, then Clone/Copy, then
|
|
comparison traits, then Hash.
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- Your struct/enum fields all implement the trait you want to derive
|
|
- The derived behavior is correct (field-by-field comparison, etc.)
|
|
- You want standard trait behavior without custom logic
|
|
|
|
**Example — before:**
|
|
```rust
|
|
struct Point { x: f64, y: f64 }
|
|
|
|
impl fmt::Debug for Point {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("Point")
|
|
.field("x", &self.x)
|
|
.field("y", &self.y)
|
|
.finish()
|
|
}
|
|
}
|
|
impl Clone for Point { ... }
|
|
impl PartialEq for Point { ... }
|
|
// 30 lines of boilerplate for standard behavior
|
|
```
|
|
|
|
**Example — after:**
|
|
```rust
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
struct Point { x: f64, y: f64 }
|
|
// One line. Same result.
|
|
```
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- You need custom comparison logic (e.g., ignore certain fields)
|
|
- Your type contains types that don't implement the trait
|
|
- Derived behavior would be wrong (e.g., floating point and Eq)
|
|
|
|
### Anti-pattern
|
|
|
|
```rust
|
|
// DON'T: Derive Eq on floats
|
|
#[derive(PartialEq, Eq)] // COMPILE ERROR: f64 doesn't impl Eq
|
|
struct Measurement { value: f64 }
|
|
|
|
// DON'T: Derive PartialEq when it would be semantically wrong
|
|
#[derive(PartialEq)]
|
|
struct Handle { id: u64, cached_name: String }
|
|
// Do you really want cached_name to affect equality?
|
|
```
|
|
|
|
---
|
|
|
|
## 3. From/Into for Type Conversions
|
|
|
|
### Source:
|
|
|
|
[library/core/src/convert/mod.rs#L460](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/convert/mod.rs#L460)
|
|
|
|
508 `From<>` implementations in the stdlib.
|
|
|
|
```rust
|
|
// Blanket impl: implementing From gives you Into for free
|
|
impl<T, U> Into<U> for T where U: From<T> {
|
|
fn into(self) -> U { U::from(self) }
|
|
}
|
|
```
|
|
|
|
### Why
|
|
|
|
From/Into is the standard conversion mechanism. Always implement
|
|
`From` (not `Into`) because you get `Into` for free via the blanket
|
|
impl. The stdlib says: "This trait must not fail. If the conversion
|
|
can fail, use TryFrom."
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- Conversion is infallible and lossless
|
|
- Converting between wrapper types (newtype → inner, inner → newtype)
|
|
- Error type conversion (see error handling patterns)
|
|
|
|
**Example — before:**
|
|
```rust
|
|
// Ad-hoc conversion methods with inconsistent names
|
|
impl Celsius {
|
|
fn to_fahrenheit(&self) -> Fahrenheit { ... }
|
|
}
|
|
impl Fahrenheit {
|
|
fn from_celsius(c: &Celsius) -> Fahrenheit { ... }
|
|
}
|
|
// Users must remember: is it .to_X() or ::from_X()?
|
|
```
|
|
|
|
**Example — after:**
|
|
```rust
|
|
impl From<Celsius> for Fahrenheit {
|
|
fn from(c: Celsius) -> Self {
|
|
Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
|
|
}
|
|
}
|
|
// Now works with: Fahrenheit::from(c), c.into(), and generic bounds
|
|
```
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- Conversion can fail (use `TryFrom`)
|
|
- Conversion is lossy (u32 → u16 can truncate)
|
|
- It's an expensive operation that shouldn't happen implicitly
|
|
|
|
---
|
|
|
|
## 4. Default for Sensible Zero Values
|
|
|
|
### Source:
|
|
|
|
[library/core/src/default.rs](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/default.rs)
|
|
|
|
226 `Default` implementations in the stdlib.
|
|
|
|
```rust
|
|
pub trait Default: Sized {
|
|
fn default() -> Self;
|
|
}
|
|
```
|
|
|
|
### Why
|
|
|
|
Provides a canonical "empty" or "zero" value. Used by `..Default::default()`
|
|
struct update syntax, `Option::unwrap_or_default()`, collection
|
|
initialization, and generic code that needs to create values.
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- Your type has an obvious "empty" state (empty string, zero, nil)
|
|
- You want `..Default::default()` to work for partial struct init
|
|
- Your type is used in generics bounded by `Default`
|
|
|
|
**Example — before:**
|
|
```rust
|
|
let config = Config {
|
|
timeout: Duration::from_secs(30),
|
|
retries: 3,
|
|
verbose: false,
|
|
port: 8080,
|
|
host: String::new(),
|
|
};
|
|
// Must specify EVERY field even for "default" values
|
|
```
|
|
|
|
**Example — after:**
|
|
```rust
|
|
#[derive(Default)]
|
|
struct Config {
|
|
timeout: Duration, // Default: 0s (may need custom impl)
|
|
retries: u32, // Default: 0
|
|
verbose: bool, // Default: false
|
|
port: u16, // Default: 0
|
|
host: String, // Default: ""
|
|
}
|
|
|
|
let config = Config {
|
|
port: 8080,
|
|
retries: 3,
|
|
..Default::default() // everything else gets defaults
|
|
};
|
|
```
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- There's no meaningful default (what's a "default" database URL?)
|
|
- The zero value would be dangerous (default timeout of 0 = infinite)
|
|
- You WANT users to think about every field
|
|
|
|
---
|
|
|
|
## 5. Display for Human-Readable Output
|
|
|
|
### Source:
|
|
|
|
[library/core/src/fmt/mod.rs#L1187](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/fmt/mod.rs#L1187)
|
|
|
|
269 Display implementations in the stdlib.
|
|
|
|
```rust
|
|
pub trait Display: PointeeSized {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
|
|
}
|
|
```
|
|
|
|
### Why
|
|
|
|
Display is for user-facing output (what `println!("{}", x)` uses).
|
|
Debug is for developer-facing output (`println!("{:?}", x)`). Every
|
|
public type should implement both. Error trait REQUIRES Display.
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- Your type will be printed/logged for end users
|
|
- Your type implements Error (Display is required)
|
|
- You want `format!("{}", value)` to work
|
|
- The output should be human-readable (not debug-formatted)
|
|
|
|
**Example — before:**
|
|
```rust
|
|
// Only Debug — ugly output for users
|
|
#[derive(Debug)]
|
|
struct Version { major: u32, minor: u32, patch: u32 }
|
|
|
|
println!("{:?}", v); // "Version { major: 1, minor: 2, patch: 3 }" — ugly
|
|
```
|
|
|
|
**Example — after:**
|
|
```rust
|
|
#[derive(Debug)]
|
|
struct Version { major: u32, minor: u32, patch: u32 }
|
|
|
|
impl fmt::Display for Version {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
|
|
}
|
|
}
|
|
|
|
println!("{}", v); // "1.2.3" — clean
|
|
```
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- There's no single canonical human representation
|
|
- The type is internal and never shown to users
|
|
- Debug output is sufficient (use `#[derive(Debug)]`)
|
|
|
|
---
|
|
|
|
## 6. Iterator Implementation
|
|
|
|
### Source:
|
|
|
|
[library/core/src/iter/traits/iterator.rs](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/iter/traits/iterator.rs)
|
|
|
|
895 Iterator implementations in the stdlib.
|
|
|
|
### Why
|
|
|
|
Implementing Iterator gives you the entire iterator ecosystem for
|
|
free: `.map()`, `.filter()`, `.collect()`, `for` loops, and
|
|
composability with every other iterator adapter.
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- Your type represents a sequence of values
|
|
- Users should be able to loop over it with `for`
|
|
- You want composability with the iterator ecosystem
|
|
|
|
**Example — before:**
|
|
```rust
|
|
struct Fibonacci { a: u64, b: u64 }
|
|
|
|
impl Fibonacci {
|
|
fn next_value(&mut self) -> u64 {
|
|
let result = self.a;
|
|
let new_b = self.a + self.b;
|
|
self.a = self.b;
|
|
self.b = new_b;
|
|
result
|
|
}
|
|
}
|
|
// Can't use with for loops, map, filter, take, collect...
|
|
```
|
|
|
|
**Example — after:**
|
|
```rust
|
|
struct Fibonacci { a: u64, b: u64 }
|
|
|
|
impl Iterator for Fibonacci {
|
|
type Item = u64;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let result = self.a;
|
|
let new_b = self.a.checked_add(self.b)?; // overflow → end
|
|
self.a = self.b;
|
|
self.b = new_b;
|
|
Some(result)
|
|
}
|
|
}
|
|
|
|
// Now works with everything:
|
|
let first_10: Vec<u64> = Fibonacci { a: 0, b: 1 }.take(10).collect();
|
|
```
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- Your type doesn't represent a sequence
|
|
- Access is random (implement Index instead)
|
|
- The "iteration" has side effects that shouldn't compose
|
|
|
|
---
|
|
|
|
## 7. Deref for Smart Pointer Transparency
|
|
|
|
### Source:
|
|
|
|
[library/core/src/ops/deref.rs#L139](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/ops/deref.rs#L139)
|
|
|
|
```rust
|
|
pub trait Deref {
|
|
type Target: ?Sized;
|
|
fn deref(&self) -> &Self::Target;
|
|
}
|
|
```
|
|
|
|
### Why
|
|
|
|
Deref coercion makes smart pointers transparent: `Box<T>` behaves
|
|
like `T`, `String` behaves like `str`, `Vec<T>` behaves like `[T]`.
|
|
The compiler inserts `.deref()` calls implicitly.
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- Your type is a smart pointer or wrapper around a single inner value
|
|
- You want method calls to "pass through" to the inner type
|
|
- There's a clear "target" type that your wrapper contains
|
|
|
|
**Example — before:**
|
|
```rust
|
|
struct MyString(Vec<u8>);
|
|
|
|
impl MyString {
|
|
fn as_str(&self) -> &str {
|
|
std::str::from_utf8(&self.0).unwrap()
|
|
}
|
|
fn len(&self) -> usize { self.as_str().len() }
|
|
fn is_empty(&self) -> bool { self.as_str().is_empty() }
|
|
fn contains(&self, pat: &str) -> bool { self.as_str().contains(pat) }
|
|
// Must manually delegate EVERY str method...
|
|
}
|
|
```
|
|
|
|
**Example — after:**
|
|
```rust
|
|
struct MyString(Vec<u8>);
|
|
|
|
impl Deref for MyString {
|
|
type Target = str;
|
|
fn deref(&self) -> &str {
|
|
std::str::from_utf8(&self.0).unwrap()
|
|
}
|
|
}
|
|
// Now ALL str methods work automatically:
|
|
// my_string.len(), my_string.contains("x"), my_string.to_uppercase()
|
|
```
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- Your type is not semantically a wrapper/pointer
|
|
- The deref target is surprising to users
|
|
- You're using Deref as inheritance (Rust doesn't have inheritance —
|
|
this abuse leads to confusing APIs)
|
|
|
|
### Anti-pattern
|
|
|
|
```rust
|
|
// DON'T: Use Deref as "inheritance"
|
|
struct Dog { name: String }
|
|
struct GuideDog { dog: Dog, handler: String }
|
|
|
|
impl Deref for GuideDog {
|
|
type Target = Dog;
|
|
fn deref(&self) -> &Dog { &self.dog }
|
|
}
|
|
// This makes GuideDog "look like" Dog — misleading.
|
|
// Use composition + explicit delegation instead.
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Sealed Traits (Restrict Implementors)
|
|
|
|
### Source:
|
|
|
|
[library/portable-simd/crates/core_simd/src/cast.rs](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/portable-simd/crates/core_simd/src/cast.rs)
|
|
|
|
```rust
|
|
mod sealed {
|
|
pub trait Sealed {}
|
|
}
|
|
pub trait SimdCast: Sealed + SimdElement {}
|
|
```
|
|
|
|
### Why
|
|
|
|
A sealed trait can only be implemented within its defining crate.
|
|
External crates can USE the trait but not IMPLEMENT it. This gives
|
|
the library author freedom to add methods without breaking changes.
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- You want to add trait methods in future versions without breaking
|
|
downstream code
|
|
- The set of valid implementors is fixed and known
|
|
- You're publishing a library and need API stability guarantees
|
|
|
|
**Example — before:**
|
|
```rust
|
|
// Public trait — anyone can implement it
|
|
pub trait Codec {
|
|
fn encode(&self, data: &[u8]) -> Vec<u8>;
|
|
// Can NEVER add decode() without breaking all implementors
|
|
}
|
|
```
|
|
|
|
**Example — after:**
|
|
```rust
|
|
mod private { pub trait Sealed {} }
|
|
|
|
pub trait Codec: private::Sealed {
|
|
fn encode(&self, data: &[u8]) -> Vec<u8>;
|
|
fn decode(&self, data: &[u8]) -> Vec<u8>; // safe to add later!
|
|
}
|
|
|
|
// Only YOUR crate can implement Codec because only YOUR crate
|
|
// can implement private::Sealed
|
|
impl private::Sealed for JsonCodec {}
|
|
impl Codec for JsonCodec { ... }
|
|
```
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- Users should be able to add their own implementations
|
|
- You're writing application code (not a library)
|
|
- The trait is intentionally an extension point
|
|
|
|
---
|
|
|
|
## 9. #[must_use] on Types and Functions
|
|
|
|
### Source:
|
|
|
|
1,963 `#[must_use]` annotations across the stdlib.
|
|
|
|
```rust
|
|
#[must_use = "this `Result` may be an `Err` variant, which should be handled"]
|
|
pub enum Result<T, E> { ... }
|
|
|
|
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
|
pub struct Map<I, F> { ... }
|
|
```
|
|
|
|
### Why
|
|
|
|
The compiler warns when a `#[must_use]` value is ignored. This
|
|
catches bugs where you call a function for its return value but
|
|
forget to use it. Iterator adapters are all `#[must_use]` because
|
|
they're lazy — creating a `.map()` does nothing until collected.
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- Ignoring the return value is always a bug
|
|
- The function/type is lazy (nothing happens without consumption)
|
|
- Builder methods that return a new value (not &mut self)
|
|
|
|
**Example — before:**
|
|
```rust
|
|
fn sorted(mut v: Vec<i32>) -> Vec<i32> {
|
|
v.sort();
|
|
v
|
|
}
|
|
|
|
let data = vec![3, 1, 2];
|
|
sorted(data); // BUG: result discarded, data is gone, nothing happened
|
|
```
|
|
|
|
**Example — after:**
|
|
```rust
|
|
#[must_use = "sorted returns the sorted vec; the original is consumed"]
|
|
fn sorted(mut v: Vec<i32>) -> Vec<i32> {
|
|
v.sort();
|
|
v
|
|
}
|
|
|
|
sorted(data); // COMPILER WARNING: unused return value of `sorted`
|
|
```
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- The function is called for side effects (e.g., `println!`)
|
|
- The return value is informational but not critical
|
|
|
|
---
|
|
|
|
## 10. Blanket Implementations
|
|
|
|
### Source:
|
|
|
|
[library/core/src/convert/mod.rs](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/convert/mod.rs) (From → Into blanket)
|
|
|
|
```rust
|
|
// Anyone who implements From gets Into for free:
|
|
impl<T, U> Into<U> for T where U: From<T> {
|
|
fn into(self) -> U { U::from(self) }
|
|
}
|
|
|
|
// Every type can be converted from itself:
|
|
impl<T> From<T> for T {
|
|
fn from(t: T) -> T { t }
|
|
}
|
|
```
|
|
|
|
### Why
|
|
|
|
Blanket impls provide functionality to ALL types that satisfy a
|
|
bound, without requiring each type to opt in. This is how Rust
|
|
scales — you implement one trait and get many others for free.
|
|
|
|
### When to Use
|
|
|
|
**Triggers:**
|
|
- Trait B can be derived from trait A for ALL types
|
|
- You want to reduce the number of impls users must write
|
|
- There's a clear mathematical relationship (From ↔ Into)
|
|
|
|
### When NOT to Use
|
|
|
|
**Don't use this when:**
|
|
- The blanket would conflict with specific implementations
|
|
- Not all types satisfying the bound actually want the blanket
|
|
- The blanket would surprise users or violate expectations
|
|
|
|
---
|
|
|
|
## Summary: Trait Design Decision Tree
|
|
|
|
```
|
|
Designing a new trait?
|
|
├── How many required methods?
|
|
│ ├── Can provide defaults built on a kernel → 1 required + many provided
|
|
│ └── Methods are independent → require each one
|
|
├── Who should implement it?
|
|
│ ├── Everyone → keep it simple, consider derive macro
|
|
│ ├── Only your crate → seal it (mod private { Sealed })
|
|
│ └── External + internal → leave it open, use #[non_exhaustive] on enums
|
|
├── Standard traits to implement on your types?
|
|
│ ├── Always: Debug
|
|
│ ├── If meaningful: Clone, PartialEq, Eq, Hash, Default
|
|
│ ├── If printable: Display
|
|
│ ├── If sequential: Iterator
|
|
│ ├── If wrapper: Deref
|
|
│ └── If convertible: From (not Into)
|
|
└── Should result be ignored?
|
|
└── Never → add #[must_use]
|
|
```
|
|
|
|
| When you need to... | Use |
|
|
|---|---|
|
|
| Provide rich API from minimal impl | One required method + provided defaults |
|
|
| Standard trait behavior | `#[derive(Debug, Clone, PartialEq, ...)]` |
|
|
| Type conversion | `impl From<Source> for Target` |
|
|
| Sensible zero/empty value | `impl Default` (or derive) |
|
|
| Human-readable output | `impl Display` |
|
|
| Iteration/composition | `impl Iterator` |
|
|
| Smart pointer transparency | `impl Deref` |
|
|
| Restrict implementors to your crate | Sealed trait pattern |
|
|
| Prevent value discard | `#[must_use]` |
|
|
| Free impls for all qualifying types | Blanket implementation |
|
|
|
|
See also:
|
|
- [error-handling.md](error-handling.md) — Error trait + From impls
|
|
- [ownership.md](ownership.md) — Clone, Copy, Drop trait usage
|
|
- [api-design.md](api-design.md) — Public trait design guidelines
|
|
|
|
<!-- PATTERN_COMPLETE -->
|