Automatic Trait Derivation
Derive Common Traits Automatically
Most structs and enums very commonly implement a number of basic traits - for example, to implement equality, cloning, enable storage in a hashmap, and provide debug formatting.
Instead of manually writing large amounts of repetitive code, you can apply the derive
↗ attribute to struct
or enum
definitions. The attribute, written #[derive(<Trait_1>, <Trait_2>, ...)]
, where <Trait_n>
should be replaced by a suitable trait name, automagically generates a basic implementation of the selected traits for the type you've annotated.
As described by Derivable traits↗, the following traits are derivable "out of the box":
- Comparison traits
Eq
↗,PartialEq
↗,Ord
↗, andPartialOrd
↗, Clone
↗, which explicitly creates a deep copy of a value,Copy
↗, which gives a type 'copy semantics' instead of 'move semantics' (less common),Hash
↗, to compute a hash of the item,Default
↗, to provide a default instance of the type,Debug
↗ to format the type for debug purposes.
The PartialEq
trait allows checking for equality and enables use of the ==
and !=
operators. The Eq
trait signals that for every value of a type, the value is equal to itself. The PartialOrd
trait allows comparisons for sorting purposes. The Ord
trait guarantees that, for any two values of a type, a valid ordering will exist. Certain primitive types, namely floats, implement PartialEq
and PartialOrd
but cannot implement Eq
and Ord
.
The Copy
trait marks types that can be duplicated (cloned) by solely copying bits stored on the stack.
The Debug
trait enables debug formatting, which you request by adding :?
within {}
placeholders in format strings.
The Default
trait provides the default value - the equivalent of an empty string or zero - for your type.
The Hash
trait takes an instance of a type of arbitrary size and map that instance to a value of fixed size (a hash code, usually an integer) using a hash function. It is commonly required to use a type as a key in a HashMap
↗ or HashSet
↗.
Note that the above traits can still be manually implemented, if a more complex behavior than what derive
provides is required.
use std::collections::HashMap; /// `derive` is a powerful tool in Rust that allows you to automatically /// implement certain traits for your structs and enums. /// /// Here we derive several common traits for a simple struct `S` that wraps an /// `i32`. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Default)] struct S(i32); fn simple_tests() { let zero = S::default(); // Courtesy of `Default`. println!("Debug formatting: {zero:?}"); // Courtesy of `Debug`. dbg!(&zero); let one = S(1); let also_one = one; // Courtesy of `Clone`, `Copy`. println!("Equality is implemented. S(1) == S(1): {}", one == also_one); // Courtesy of `PartialEq`, `Eq`. println!("Ordering is implemented. S(1) < S(2): {}", one < S(2)); // Courtesy of `PartialOrd`, `Ord`. } // The `Hash` trait enables types to be hashed, which means they can // be used as keys in hash-based collections like `HashMap` and `HashSet`. // When you use `#[derive(Hash)]` on a struct, the derived implementation // will typically hash each field of the struct in sequence and combine these // hashes into a single hash value for the entire struct instance. fn hash() { let mut map = HashMap::new(); let s1 = S(10); let s2 = S(20); // Use instances of `S` as keys: map.insert(s1, "Value for S(10)"); map.insert(s2, "Value for S(20)"); // Retrieve a value using an `S` key: if let Some(value) = map.get(&S(10)) { println!("Found: {value}"); // Output: Found: Value for S(10). } } fn main() { simple_tests(); hash(); }
Install and use the cargo-expand
↗ utility to see the exact code that is generated by derive
for your specific type.
It is possible to implement derive
for your own custom traits through procedural macros.
Derive Additional Traits with derive_more
The derive_more↗ crate derive lots of additional, commonly used traits and static methods for both structs and enums:
- Arithmetic Traits:
Add
↗,Sub
↗,Mul
↗,Div
↗,AddAssign
,SubAssign
,MulAssign
, etc. for custom numeric types, - Conversion Traits:
From
↗,Into
↗,TryFrom
↗,TryInto
↗ for easy type conversions, - Smart Pointer Traits:
Deref
↗,DerefMut
↗ for implementing container types, Display
↗ andError
↗ for better formatting and error handling,Constructor
to auto-generate constructors for structs,- Boolean Operators
Not
↗,BitAnd
↗,BitOr
↗, etc.
You can also implement derive
for your own traits through procedural macros.
//! The `derive_more` crate extends Rust's built-in derive functionality to
//! provide more automatic implementations for common traits.
//!
//! Add to your `Cargo.toml`:
//! ```toml
//! [dependencies]
//! derive_more = "2.0.1" # Or latest
//! ```
use derive_more::Add;
use derive_more::AddAssign;
use derive_more::Constructor;
use derive_more::Deref;
use derive_more::DerefMut;
use derive_more::Display;
use derive_more::From;
use derive_more::Into;
use derive_more::Sub;
use derive_more::SubAssign;
/// Basic numeric type with arithmetic operations.
#[derive(Add, AddAssign, PartialEq, Debug)]
struct Point {
x: i32,
y: i32,
}
/// Newtype pattern with conversion traits.
#[derive(From, Into, Display, Debug)]
struct UserId(u64);
/// Struct with a constructor.
#[derive(Constructor, Debug)]
struct User {
id: UserId,
name: String,
active: bool,
}
// Using `Deref` and `DerefMut`.
#[derive(Deref, DerefMut, Debug)]
struct Stack<T> {
#[deref]
#[deref_mut]
items: Vec<T>,
}
fn main() {
// Using `Add` and `AddAssign`.
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 3, y: 4 };
let p3 = p1 + p2; // Thanks to `Add`.
assert_eq!(p3, Point { x: 4, y: 6 });
let mut p4 = Point { x: 5, y: 6 };
p4 += Point { x: 1, y: 1 }; // Thanks to `AddAssign`.
assert_eq!(p4, Point { x: 6, y: 7 });
// Using `From` and `Into`.
let user_id = UserId::from(12345);
let raw_id: u64 = user_id.into();
assert_eq!(raw_id, 12345);
// Using `Display`.
println!("User ID: {}", UserId(67890)); // Prints "User ID: 67890".
// Using `Constructor`.
let user = User::new(UserId(12345), "Alice".to_string(), true);
println!("{user:?}"); // User { id: UserId(12345), name: "Alice", active: true }.
// Using `Deref` and `DerefMut`.
let mut stack = Stack {
items: vec![1, 2, 3],
};
stack.push(4); // Using `Vec`'s push method through `DerefMut`.
assert_eq!(stack.len(), 4); // Using `Vec`'s len method through `Deref`.
}
// More complex example: Multiple derives on a single type.
#[derive(
Add, AddAssign, Sub, SubAssign, From, Into, Display, Debug, Clone, Copy,
)]
struct Amount(f64);
// Custom error type with derive_more's `Error` trait.
// `std::error::Error` requires `std::fmt::Debug` and `std::fmt::Display`.
#[derive(Debug, derive_more::Error, Display, From)]
enum AppError {
#[display("Database error")]
DatabaseError,
#[display("Insufficient funds")]
InsufficientFundsError,
}
// Using multiple types with `derive_more`.
fn transfer_amount(
from: &mut Amount,
to: &mut Amount,
value: Amount,
) -> Result<(), AppError> {
if from.0 < value.0 {
return Err(AppError::InsufficientFundsError);
}
*from -= value;
*to += value;
Ok(())
}
References
Related Topics
- Attributes.
- Macros.
- Rust Patterns.