These traits decide whether your types can be printed, compared, hashed, copied or converted, so every type you write needs the right set.
The standard library defines a set of traits that nearly every type should consider. Most can be generated with `#[derive(...)]`, which implements them field by field (so each field must implement the trait too).
Debug:{:?}formatting for developers. Derive it on almost everything.Clone: explicit.clone().Copy: implicit bitwise copy on assignment; only for small types whose fields are allCopy, and it requiresClone.PartialEq/Eq:==.Eqpromises equality is reflexive, which floats break (NaN != NaN), sof64is onlyPartialEq.PartialOrd/Ord:<, sorting,max. Derived ordering compares fields in declaration order, and enum variants in declaration order.Hash: needed forHashMapkeys, together withEq. Keep it consistent withPartialEq: equal values must hash equally.Default:T::default()and..Default::default()in struct literals. Enums can derive it by marking one variant#[default].
Some must be written by hand:
- `Display`: user-facing
{}formatting. Implementfmt(&self, f: &mut fmt::Formatter) -> fmt::Resultusingwrite!. It also gives you.to_string()for free. - `From<T>`: infallible conversion. Implementing
From<A> for Bautomatically providesInto<B> for A, so implementFromand acceptimpl Into<B>in APIs when convenient.TryFromis the fallible version.
Other traits you will see in signatures: AsRef<str> (cheap reference conversion), Deref, Drop (custom cleanup when a value goes out of scope), Iterator and IntoIterator, Send/Sync for threads, and FromStr, which powers str::parse.
Example
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
enum Level { #[default] Low, Mid, High }
#[derive(Debug, Clone, PartialEq, Default)]
struct Reading { celsius: f64, level: Level }
impl fmt::Display for Reading {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:.1}°C ({:?})", self.celsius, self.level)
}
}
impl From<f64> for Reading {
fn from(celsius: f64) -> Self {
Reading { celsius, level: if celsius > 30.0 { Level::High } else { Level::Mid } }
}
}
fn main() {
let r: Reading = 31.5.into(); // Into comes free with From<f64>
println!("{r} / {r:?}"); // 31.5°C (High) / Reading { celsius: 31.5, level: High }
println!("{}", Level::Low < Level::High); // true: variant order
println!("{}", Reading::default()); // 0.0°C (Low)
}Remember
- Derive
Debug,Clone,PartialEq(and friends) unless you have a reason not to Copyonly for small plain-data types;EqandHashmust agree withPartialEq- Implement
Displayfor user-facing output andFromfor conversions; you getto_stringandIntofree
Watch out for
- Implementing
Intodirectly instead ofFrom, which loses the blanket conversion.