18. The standard traits you will implement every week
| Trait | What it gives you | Usually |
|---|---|---|
Debug |
{:?} formatting |
#[derive(Debug)] on almost everything |
Display |
{} formatting, .to_string() |
implement by hand for user-facing types |
Clone, Copy |
.clone(), implicit copy |
derive |
PartialEq, Eq |
== |
derive; Eq only if equality is reflexive (not for floats) |
Hash |
usable as a HashMap key |
derive together with Eq |
PartialOrd, Ord |
<, sorting, BTreeMap keys |
derive (orders by fields, top to bottom) |
Default |
T::default(), ..Default::default() |
derive or implement |
From<T> / Into<T> |
infallible conversion | implement From; you get Into for free |
TryFrom<T> |
fallible conversion | validation at boundaries (Ch 53) |
FromStr |
"..".parse::<T>() |
config and CLI parsing |
AsRef<T> |
cheap reference conversion | flexible function parameters |
Deref |
*x, method forwarding |
smart pointers only; do not use it to fake inheritance |
Drop |
destructor | resource handles |
Iterator, IntoIterator |
for loops, adapters |
Ch 21 |
Send, Sync |
thread safety | implemented automatically (Ch 28) |
use std::fmt;
use std::ops::Add;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
struct Bytes(u64);
impl fmt::Display for Bytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const UNITS: [&str; 5] = ["B", "KiB", "MiB", "GiB", "TiB"];
let mut v = self.0 as f64;
let mut i = 0;
while v >= 1024.0 && i < UNITS.len() - 1 { v /= 1024.0; i += 1; }
if i == 0 { write!(f, "{} B", self.0) } else { write!(f, "{v:.1} {}", UNITS[i]) }
}
}
impl Add for Bytes { // operator overloading is just a trait
type Output = Bytes;
fn add(self, rhs: Bytes) -> Bytes { Bytes(self.0 + rhs.0) }
}
impl From<u64> for Bytes { fn from(n: u64) -> Self { Bytes(n) } }
fn main() {
let total: Bytes = Bytes(1536) + 1_048_576.into(); // `.into()` uses From<u64>
let mut sizes = vec![Bytes(10), Bytes(3), Bytes::default()];
sizes.sort(); // Ord derive
println!("{total} {:?} {}", sizes, Bytes(512)); // 1.0 MiB [Bytes(0), Bytes(3), Bytes(10)] 512 B
}
Eq and Hash must agree. If a == b, then hash(a) == hash(b). If
you implement PartialEq by hand (for example, case-insensitive), implement
Hash by hand in the same way. Otherwise HashMap gives wrong answers
without any error.