Frontend Labs
⋯

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.


Generated from real-life-rust-guide.md · 8 Oct 2026 16:36