Rust Common Traits
On the Traits page you wrote your own traits. But the standard library already comes with a set of traits that almost every Rust program uses. They answer simple questions about a type: Can I print it? Can I copy it? Can I compare it? Can I make an empty one?
Think of them like badges on a uniform. Each badge says “this type knows how to do X”. Many of these badges you can get for free with #[derive(...)]. Others you write by hand. This page walks through the ones you’ll meet most often, and ends with a table you can come back to.
Debug: printing for developers
Section titled “Debug: printing for developers”Debug lets you print a value with {:?}. It’s meant for you, the developer — for checking values while you build and fix your code. You almost never write it by hand; just derive it. Use {:#?} for a “pretty” version with one field per line, which is easier to read for big structs:
#[derive(Debug)]struct Book { title: String, pages: u32,}
fn main() { let book = Book { title: String::from("Dune"), pages: 412 }; println!("{:?}", book); println!("{:#?}", book);}Output:
Book { title: "Dune", pages: 412 }Book { title: "Dune", pages: 412,}The dbg! macro you may see in other code also uses Debug.
Display: printing for users
Section titled “Display: printing for users”Display is what {} uses. It’s for user-facing text — the message a person reads on the screen. Rust can’t guess what that should look like, so there’s no derive. You write it yourself by implementing fmt::Display:
use std::fmt;
struct Book { title: String, author: String,}
impl fmt::Display for Book { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} by {}", self.title, self.author) }}
fn main() { let book = Book { title: String::from("Dune"), author: String::from("Frank Herbert"), };
println!("{}", book);
let text = book.to_string(); println!("That text has {} characters", text.len());}Output:
Dune by Frank HerbertThat text has 21 characterswrite! works like println!, but writes into the formatter f instead of the screen. A nice bonus: once a type has Display, you get .to_string() for free. You’ll see Display again on the Custom Errors page, where it gives errors a friendly message.
Clone and Copy
Section titled “Clone and Copy”Both make a duplicate of a value, but in different ways.
Clonegives you a.clone()method. You call it on purpose, and it can do real work, like copying all the text in aString.Copymakes a duplicate automatically every time you assign or pass the value. It’s only allowed for small, simple types that can be copied bit by bit: numbers,bool,char, and structs made only of those.
#[derive(Debug, Clone, Copy)]struct Point { x: i32, y: i32,}
#[derive(Debug, Clone)]struct User { name: String,}
fn main() { let p1 = Point { x: 1, y: 2 }; let p2 = p1; // Copy: p1 is still usable println!("{:?} and {:?}", p1, p2);
let u1 = User { name: String::from("Ana") }; let u2 = u1.clone(); // Clone: we ask for a copy println!("{:?} and {:?}", u1, u2);}Output:
Point { x: 1, y: 2 } and Point { x: 1, y: 2 }User { name: "Ana" } and User { name: "Ana" }Without Copy, let p2 = p1; would move p1, and you couldn’t use it afterwards (see Ownership). Note that Copy always needs Clone too — that’s why you derive both together.
PartialEq and Eq: comparing with ==
Section titled “PartialEq and Eq: comparing with ==”PartialEq lets you use == and !=. When you derive it, two values are equal if all their fields are equal:
#[derive(Debug, PartialEq)]struct Point { x: i32, y: i32,}
fn main() { let a = Point { x: 1, y: 2 }; let b = Point { x: 1, y: 2 }; let c = Point { x: 5, y: 0 };
println!("a == b: {}", a == b); println!("a != c: {}", a != c);}Output:
a == b: truea != c: trueSo what is Eq? It adds no new methods. It’s a promise: “every value is always equal to itself”. That sounds obvious, but floats break it — in Rust, f64::NAN == f64::NAN is false. So f64 has PartialEq but not Eq. You need Eq (plus Hash) to use your type as a HashMap key or in a HashSet. If your struct has no float fields, derive both.
PartialOrd and Ord: comparing with < and >
Section titled “PartialOrd and Ord: comparing with < and >”PartialOrd lets you use <, >, <=, and >=. Ord adds a full ordering, which is what .sort(), .max(), and .min() need. When you derive them, Rust compares fields from top to bottom: first field first, and only if those are equal does it look at the next one.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]struct Version { major: u32, minor: u32, patch: u32,}
fn main() { let old = Version { major: 1, minor: 2, patch: 0 }; let new = Version { major: 1, minor: 10, patch: 3 }; println!("old < new: {}", old < new);
let mut versions = vec![ Version { major: 2, minor: 0, patch: 0 }, new, old, ]; versions.sort();
for v in &versions { println!("{}.{}.{}", v.major, v.minor, v.patch); }}Output:
old < new: true1.2.01.10.32.0.0Because the fields are compared in order, the order you write the fields in matters. Here major is the most important, which is exactly right for version numbers. For enums, variants listed first count as “smaller”. Just like with Eq, floats only get PartialOrd, not Ord — the Sorting page shows how to sort floats anyway.
Default: a sensible starting value
Section titled “Default: a sensible starting value”Default gives your type a ::default() function that builds a “starting” value. When derived, each field gets its own default: 0 for numbers, false for bool, an empty String, an empty Vec. The ..Default::default() syntax lets you set a few fields and fill in the rest:
#[derive(Debug, Default)]struct Settings { volume: u32, muted: bool, username: String,}
fn main() { let basic = Settings::default(); println!("{:?}", basic);
let custom = Settings { volume: 80, ..Default::default() }; println!("{:?}", custom);}Output:
Settings { volume: 0, muted: false, username: "" }Settings { volume: 80, muted: false, username: "" }If you want different starting values (say, volume 50), skip the derive and write impl Default for Settings yourself, with a fn default() -> Self that returns the values you want.
From and Into: converting between types
Section titled “From and Into: converting between types”From describes how to build one type from another. You’ve used it already: String::from("hi"). When you implement From, Rust gives you Into for free, going the other way:
struct Celsius(f64);struct Fahrenheit(f64);
impl From<Celsius> for Fahrenheit { fn from(c: Celsius) -> Self { Fahrenheit(c.0 * 9.0 / 5.0 + 32.0) }}
fn main() { let boiling = Fahrenheit::from(Celsius(100.0)); println!("Water boils at {}°F", boiling.0);
let body: Fahrenheit = Celsius(37.0).into(); println!("Body temperature is {:.1}°F", body.0);}Output:
Water boils at 212°FBody temperature is 98.6°FWith .into(), Rust needs to know the target type, which is why we wrote let body: Fahrenheit.
How ? uses From. This is where From really shines. When the ? operator meets an error, it calls From::from to turn that error into the error type your function returns. So if you implement From for your own error type, ? converts errors for you:
use std::num::ParseIntError;
#[derive(Debug)]enum AppError { BadNumber(ParseIntError),}
impl From<ParseIntError> for AppError { fn from(e: ParseIntError) -> Self { AppError::BadNumber(e) }}
fn parse_age(text: &str) -> Result<u32, AppError> { let age: u32 = text.parse()?; // ParseIntError becomes AppError here Ok(age)}
fn main() { println!("{:?}", parse_age("42")); println!("{:?}", parse_age("abc"));}Output:
Ok(42)Err(BadNumber(ParseIntError { kind: InvalidDigit }))The Custom Errors page builds on this idea in more detail.
Operator overloading with Add
Section titled “Operator overloading with Add”In Rust, a + b is really a call to the add method of the std::ops::Add trait. Implement it for your type, and + just works. type Output says what type the addition produces:
use std::ops::Add;
#[derive(Debug, Clone, Copy, PartialEq)]struct Point { x: i32, y: i32,}
impl Add for Point { type Output = Point;
fn add(self, other: Point) -> Point { Point { x: self.x + other.x, y: self.y + other.y } }}
fn main() { let a = Point { x: 1, y: 2 }; let b = Point { x: 3, y: 4 }; println!("{:?}", a + b);}Output:
Point { x: 4, y: 6 }The same idea works for Sub (-), Mul (*), Neg (unary -), and AddAssign (+=). Use this when the meaning is obvious — adding two points or two amounts of money. If a reader would have to guess what + means for your type, a normal method with a clear name is better.
Which traits should I derive?
Section titled “Which traits should I derive?”Here is a quick guide for a typical struct:
| Trait | Derive? | What you get |
|---|---|---|
Debug |
Almost always | {:?} and {:#?} printing |
Clone |
Usually | .clone() |
Copy |
Only for small types with no String, Vec, or Box fields |
Automatic copies instead of moves |
PartialEq |
Usually | == and != |
Eq |
When there are no float fields | Needed for HashMap keys (with Hash) |
Hash |
When used as a HashMap key or in a HashSet |
Hashing |
PartialOrd, Ord |
When values have a natural order | <, >, .sort(), .max() |
Default |
When an “empty” value makes sense | ::default() and ..Default::default() |
Display |
Can’t derive — write it by hand | {} printing and .to_string() |
A very common starting line is #[derive(Debug, Clone, PartialEq)]. Add more when you need them — the compiler will tell you when one is missing.
Common mistakes
Section titled “Common mistakes”Deriving Copy on a struct with a String.
#[derive(Clone, Copy)]struct User { name: String, age: u32,}error[E0204]: the trait `Copy` cannot be implemented for this type --> src/main.rs:2:8 |1 | #[derive(Clone, Copy)] | ---- in this derive macro expansion2 | struct User { | ^^^^3 | name: String, | ------------ this field does not implement `Copy`A String owns memory on the heap, so it can’t be copied bit by bit. Remove Copy and use .clone() when you need a duplicate.
Deriving Eq on a struct with a float.
#[derive(Debug, PartialEq, Eq)]struct Product { name: String, price: f64 }error[E0277]: the trait bound `f64: Eq` is not satisfied --> src/main.rs:2:32 |1 | #[derive(Debug, PartialEq, Eq)] | -- in this derive macro expansion2 | struct Product { name: String, price: f64 } | ^^^^^^^^^^ the trait `Eq` is not implemented for `f64`Keep PartialEq and drop Eq. Or store the price as whole cents in a u64, which is also safer for money.
Practice
Section titled “Practice”1. Sort a to-do list
Section titled “1. Sort a to-do list”Make a Task struct with a priority: u8 and a title: String. Derive the traits you need so that you can sort a Vec<Task>, print each task with {:?}, and compare two tasks with ==. Lower numbers are more urgent, so they should come first. Hint: .sort() needs Ord, and Ord needs Eq, PartialEq and PartialOrd too. Put priority first so it’s compared first.
Show solution
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]struct Task { priority: u8, title: String,}
fn main() { let mut tasks = vec![ Task { priority: 3, title: String::from("Water plants") }, Task { priority: 1, title: String::from("Pay rent") }, Task { priority: 2, title: String::from("Reply to Sam") }, ];
let copy = tasks[1].clone(); println!("Same task? {}", copy == tasks[1]);
tasks.sort(); for task in &tasks { println!("{:?}", task); }}Output:
Same task? trueTask { priority: 1, title: "Pay rent" }Task { priority: 2, title: "Reply to Sam" }Task { priority: 3, title: "Water plants" }2. Money that prints and adds
Section titled “2. Money that prints and adds”Make a Money struct that stores whole cents: u64. Implement From<u64> so 350.into() works, Display so it prints like $3.50, and Add so two amounts can be added with +. Hint: dollars are cents / 100 and the rest is cents % 100. Print the rest with {:02} so 5 cents shows as 05. Derive Copy so you can still print the amounts after adding them.
Show solution
use std::fmt;use std::ops::Add;
#[derive(Debug, Clone, Copy, PartialEq)]struct Money { cents: u64,}
impl From<u64> for Money { fn from(cents: u64) -> Self { Money { cents } }}
impl fmt::Display for Money { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "${}.{:02}", self.cents / 100, self.cents % 100) }}
impl Add for Money { type Output = Money;
fn add(self, other: Money) -> Money { Money { cents: self.cents + other.cents } }}
fn main() { let coffee: Money = 350.into(); let cake = Money::from(405); let total = coffee + cake;
println!("Coffee: {coffee}"); println!("Cake: {cake}"); println!("Total: {total}");}Output:
Coffee: $3.50Cake: $4.05Total: $7.55Debug({:?}) is for developers and is almost always derived.Display({}) is for users and is written by hand.Clonecopies on request with.clone().Copycopies automatically, but only for simple types.PartialEq/Eqgive you==.PartialOrd/Ordgive you<and sorting, comparing fields from top to bottom.Defaultbuilds a starting value;..Default::default()fills in the fields you didn’t set.- Implement
Fromand you getIntofor free. The?operator usesFromto convert errors. - Implement
std::ops::Add(and friends) to make+work with your own types.
Want to go further? Advanced Traits explains associated types like type Output, default type parameters, and how to implement a trait for a type you didn’t write.
Quick check
Section titled “Quick check”Quick check
1. Which trait does {} in println! use?
2. Why can't a struct with a String field derive Copy?
3. When you derive PartialOrd, how are two structs compared?
4. You implemented From<ParseIntError> for AppError. What does that let ? do?
5. Which trait do you implement to make a + b work for your own type?
Score: 0 / 5
Read more
Section titled “Read more”- Derivable Traits — The Rust Book (Appendix C): every trait you can
#[derive], what the derived version does, and when you’d want it. std::fmt— formatting and Display: all the formatting options, like width, padding and{:#?}, plus howDisplayandDebugfit in.FromandInto— Rust by Example: short runnable examples of converting between your own types.std::ops— operator traits: the full list of operators you can overload, fromAddtoIndex.std::cmp— comparison traits:PartialEq,Eq,PartialOrdandOrdin detail, with the rules each one promises.