Appendix C: Derivable Traits
- Appendix C: Derivable Traits * PartialEq * Eq * PartialOrd * Ord * Clone * Copy
In various places in the book, we’ve discussed the derive attribute, which
you can apply to a struct or enum definition.
The
deriveattribute generates code that will implement a trait with its own default implementation on the type you’ve annotated with thederivesyntax.
In this appendix, we provide a reference of all the traits in the standard
library that you can use with derive. Each section covers:
- What operators and methods deriving this trait will enable
- What the implementation of the trait provided by
derivedoes - What implementing the trait signifies about the type
- The conditions in which you’re allowed or not allowed to implement the trait
- Examples of operations that require the trait
If you want different behavior from that provided by the
deriveattribute, consult the standard library documentation for each trait for details of how to manually implement them.
-
These traits listed here are the only ones defined by the standard library that can be implemented on your types using
derive. -
Other traits defined in the standard library don’t have sensible default behavior, so it’s up to you to implement them in the way that makes sense for what you’re trying to accomplish.
An example of a trait that can’t be derived is Display, which handles
formatting for end users.
You should always consider the appropriate way to display a type to an end user:
- What parts of the type should an end user be allowed to see?
- What parts would they find relevant?
- What format of the data would be most relevant to them?
- The Rust compiler doesn’t have this insight, so it can’t provide appropriate default behavior for you.
The list of derivable traits provided in this appendix is not comprehensive:
- libraries can implement
derivefor their own traits, making the list of traits you can usederivewith truly open-ended. - Implementing
deriveinvolves using a procedural macro, which is covered in the “Macros” section of Chapter 19.
Debug for Programmer Output
-
The
Debugtrait enables debug formatting in format strings, which you indicate by adding:?within{}placeholders. -
The
Debugtrait allows you to print instances of a type for debugging purposes, so you and other programmers using your type can inspect an instance at a particular point in a program’s execution. -
The
Debugtrait is required, for example, in use of theassert_eq!macro.
- This macro prints the values of instances given as arguments if the equality assertion fails so programmers can see why the two instances weren’t equal.
PartialEq and Eq for Equality Comparisons
PartialEq
-
The
PartialEqtrait allows you to compare instances of a type to check for equality and enables use of the==and!=operators. -
Deriving
PartialEqimplements theeqmethod.
- When
PartialEqis derived on structs, two instances are equal only if all fields are equal, and the instances are not equal if any fields are not equal. - When derived on enums, each variant is equal to itself and not equal to the other variants.
- The
PartialEqtrait is required, for example, with the use of theassert_eq!macro, which needs to be able to compare two instances of a type for equality.
Eq
- The
Eqtrait has no methods.
- Its purpose is to signal that for every value of the annotated type, the value is equal to itself.
- The
Eqtrait can only be applied to types that also implementPartialEq, although not all types that implementPartialEqcan implementEq.
-
One example of this is floating point number types: the implementation of floating point numbers states that two instances of the not-a-number (
NaN) value are not equal to each other. -
An example of when
Eqis required is for keys in aHashMap<K, V>so theHashMap<K, V>can tell whether two keys are the same.
PartialOrd and Ord for Ordering Comparisons
PartialOrd
The PartialOrd trait allows you to compare instances of a type for sorting
purposes.
- A type that implements
PartialOrdcan be used with the<,>,<=, and>=operators. - You can only apply the
PartialOrdtrait to types that also implementPartialEq.
Deriving PartialOrd implements the partial_cmp method, which returns an
Option<Ordering> that will be None when the values given don’t produce an
ordering.
An example of a value that doesn’t produce an ordering, even though most values of that type can be compared, is the not-a-number (
NaN) floating point value.
Calling partial_cmp with any floating point number and the NaN
floating point value will return None.
When derived on structs, PartialOrd compares two instances by comparing the
value in each field in the order in which the fields appear in the struct
definition.
When derived on enums, variants of the enum declared earlier in the enum definition are considered less than the variants listed later.
The PartialOrd trait is required, for example, for the gen_range method
from the rand crate that generates a random value in the range specified by a
range expression.
Ord
The Ord trait allows you to know that for any two values of the annotated
type, a valid ordering will exist.
The Ord trait implements the cmp method,
which returns an Ordering rather than an Option<Ordering> because a valid
ordering will always be possible.
You can only apply the Ord trait to types
that also implement PartialOrd and Eq (and Eq requires PartialEq).
When
derived on structs and enums, cmp behaves the same way as the derived
implementation for partial_cmp does with PartialOrd.
An example of when
Ordis required is when storing values in aBTreeSet<T>, a data structure that stores data based on the sort order of the values.
Clone and Copy for Duplicating Values
Clone
The Clone trait allows you to explicitly create a deep copy of a value, and
the duplication process might involve running arbitrary code and copying heap
data.
See the “Ways Variables and Data Interact: Clone” section in Chapter 4 for more information on
Clone.
Deriving Clone implements the clone method, which when implemented for the
whole type, calls clone on each of the parts of the type.
This means all the fields or values in the type must also implement
Cloneto deriveClone.
An example of when Clone is required is when calling the to_vec method on a
slice.
The slice doesn’t own the type instances it contains, but the vector returned from
to_vecwill need to own its instances, soto_veccallscloneon each item. Thus, the type stored in the slice must implementClone.
Copy
The Copy trait allows you to duplicate a value by only copying bits stored on
the stack; no arbitrary code is necessary.
See the “Stack-Only Data: Copy” section in Chapter 4 for more information on
Copy.
The Copy trait doesn’t define any methods to prevent programmers from
overloading those methods and violating the assumption that no arbitrary code
is being run.
That way, all programmers can assume that copying a value will be very fast.
You can derive Copy on any type whose parts all implement Copy.
A type that implements
Copymust also implementClone, because a type that implementsCopyhas a trivial implementation ofClonethat performs the same task asCopy.
The Copy trait is rarely required;
types that implement Copy have
optimizations available, meaning you don’t have to call clone, which makes
the code more concise.
Everything possible with Copy you can also accomplish with Clone, but the
code might be slower or have to use clone in places.
Hash for Mapping a Value to a Value of Fixed Size
The Hash trait allows you to take an instance of a type of arbitrary size and
map that instance to a value of fixed size using a hash function.
Deriving
Hashimplements thehashmethod.
The derived implementation of the hash
method combines the result of calling hash on each of the parts of the type,
meaning all fields or values must also implement Hash to derive Hash.
An example of when
Hashis required is in storing keys in aHashMap<K, V>to store data efficiently.
Default for Default Values
The Default trait allows you to create a default value for a type.
Deriving
Defaultimplements thedefaultfunction.
The derived implementation of the
default function calls the default function on each part of the type,
meaning all fields or values in the type must also implement Default to
derive Default.
The
Default::defaultfunction is commonly used in combination with the struct update syntax discussed in the “Creating Instances From Other Instances With Struct Update Syntax” section in Chapter 5.
You can customize a few fields of a struct and then
set and use a default value for the rest of the fields by using
..Default::default().
The Default trait is required when you use the method unwrap_or_default on
Option<T> instances, for example.
If the
Option<T>isNone, the methodunwrap_or_defaultwill return the result ofDefault::defaultfor the typeTstored in theOption<T>.