C.W.K.
Stream
Lesson 07 of 07 · published

The Standard Traits You'll Use Daily

~12 min · traits, std, derive, from-into

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

You'll spend more time implementing standard-library traits than writing your own. A handful show up constantly — knowing them turns 'why won't this compile?' into 'oh, I need to derive that.'

The derivable big five

Debug (print with {:?}), Clone (explicit deep copy), Copy (implicit bitwise copy), PartialEq (the == operator), and Default (a ::default() value) can all be auto-generated with #[derive(...)]. Put them above a struct and the compiler writes the obvious implementation. You'll type #[derive(Debug, Clone, PartialEq)] on reflex.

Display vs Debug

Debug is for developers (derivable, {:?}); Display is for end users (you implement it by hand, {}). The split is deliberate: the messy internal view and the polished user-facing view are different jobs, so Rust makes them different traits.

From and Into: conversions

Implement From<A> for B and you get B::from(a)and a.into() for free, because Into is auto-derived from From. This pair is everywhere: error conversions (the ? operator uses From to convert error types), type wrappers, ergonomic constructors. Implement From, never Into directly.

Derive first, implement when you must. Reach for #[derive(...)] for the mechanical traits (Debug, Clone, PartialEq, Default). Hand-implement only the ones that need real logic — Display for human formatting, From for conversions, Iterator for custom iteration. Let the compiler write the boilerplate.

This is the bridge to everything

From here on, the standard library is your playground, and it's all traits: Iterator powers the Collections track, From powers error handling, Send/Sync power concurrency. You now have the one concept — traits — that the rest of Rust is built from.

Code

Derive the mechanical, implement the meaningful·rust
use std::fmt;

#[derive(Debug, Clone, PartialEq)] // mechanical traits: derived
struct Celsius(f64);
struct Fahrenheit(f64);

impl fmt::Display for Celsius {       // human formatting: hand-written
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}°C", self.0)
    }
}
impl From<Celsius> for Fahrenheit {   // conversion: From gives Into for free
    fn from(c: Celsius) -> Self {
        Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
    }
}

fn main() {
    let c = Celsius(100.0);
    let f: Fahrenheit = c.clone().into(); // .into() works because From exists
    println!("{c} = {}°F", f.0);          // Display gives us {c}
}

External links

Exercise

Make a Money(u64) newtype. Derive Debug, Clone, Copy, PartialEq. Hand-implement Display to print it as $1.23 (cents to dollars). Implement From<u64> for Money. Then confirm == works, {} prints nicely, and 42u64.into() builds a Money.
Hint
Derive the four mechanical traits in one attribute; write Display by hand for the dollar formatting; implement From<u64> and .into() comes free. That mix — derive the boring, implement the meaningful — is everyday Rust.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.