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.
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
, 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
- Derive⮳.
Related Topics
- Attributes.
- Rust Patterns.