Derivative
This crate provides a set of alternative #[derive]
attributes for Rust.
Examples
derivative uses attributes to make it possible to derive more implementations
than the built-in derive(Trait)
. Here are a few examples of stuffs you cannot
just derive
.
You can derive Default
on enumerations:
With derivative |
|
---|---|
|
|
You can use different default values for some fields:
With derivative |
|
---|---|
|
|
Want a transparent Debug
implementation for your wrapper? We got that:
With derivative |
|
---|---|
|
|
Need to ignore a field? We got that too:
With derivative |
|
---|---|
|
|
Custom attributes
The Copy
and Clone
traits support the following attributes:
- Container attributes
- Field attributes
clone_from
The Clone
trait has a default implementation for clone_from
and
derive(Clone)
never implements that method. derivative can implement it if
asked explicitly.
Note that while the generated implementation is good for structures, it might
not be very efficient for enumerations. What it does is check if both self
and the clone-from value have the same variant, if they have, use clone_from
on the members, otherwise fallback to *self = other.clone();
. Ask yourself if
you really need this.
Custom bound
As most other traits, Copy
and Debug
support a custom bound on container
and fields. See Debug
's documentation for more
information.
Limitations
rustc can optimize derive(Clone, Copy)
to generate faster, smaller code.
So does derivative. But rustc does not know about derivative(Copy)
and
would not optimize #[derivative(Copy)] #[derive(Clone)]
.
To avoid that issue, you should avoid deriving Clone
using rustc's default
derive
and Copy
using derivative
. derivative will error if it detects
that, but can't always do it.
Custom attributes
The Debug
trait supports the following attributes:
- Container attributes
- Variant attributes
- Field attributes
Ignoring a field
You can use derivative to hide fields from a structure or enumeration Debug
implementation:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Derivative)] #[derivative(Debug)] struct Foo { foo: u8, #[derivative(Debug="ignore")] bar: u8, } println!("{:?}", Foo { foo: 42, bar: 1 }); // Foo { foo: 42 } }
Hiding newtypes
You can use derivative to automatically unwrap newtypes and enumeration variants with only one field:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Derivative)] #[derivative(Debug="transparent")] struct A(isize); #[derive(Derivative)] #[derivative(Debug)] enum C { Foo(u8), #[derivative(Debug="transparent")] Bar(u8), } println!("{:?}", A(42)); // 42 println!("{:?}", C::Bar(42)); // 42 // But: println!("{:?}", C::Foo(42)); // Foo(42) }
Format with
You can pass a field to a format function:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; mod path { pub struct SomeTypeThatMightNotBeDebug; pub mod to { pub fn my_fmt_fn(_: &super::SomeTypeThatMightNotBeDebug, _: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { unimplemented!() } } } use path::SomeTypeThatMightNotBeDebug; #[derive(Derivative)] #[derivative(Debug)] struct Foo { foo: u32, #[derivative(Debug(format_with="path::to::my_fmt_fn"))] bar: SomeTypeThatMightNotBeDebug, } }
The field bar
will be displayed with path::to::my_fmt_fn(&bar, &mut fmt)
where fmt
is the current Formatter
.
The function must the following prototype:
fn fmt(&T, &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>;
Custom bound
Usually, derivative will add a T: Debug
bound for each type parameter T
of the current type. If you do not want that, you can specify an explicit bound:
- Either on the type. This replaces all bounds:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; trait MyDebug { fn my_fmt(&self, _: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>; } use std::fmt::Debug; #[derive(Derivative)] #[derivative(Debug(bound="T: Debug, U: MyDebug"))] struct Foo<T, U> { foo: T, #[derivative(Debug(format_with="MyDebug::my_fmt"))] bar: U, } }
- Or on a field. This replaces the bound derivative guessed for that field. The example below is equivalent to the above:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; trait MyDebug { fn my_fmt(&self, _: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>; } #[derive(Derivative)] #[derivative(Debug)] struct Foo<T, U> { foo: T, #[derivative(Debug(format_with="MyDebug::my_fmt", bound="U: MyDebug"))] bar: U, } }
With bound=""
it is possible to remove any bound for the type. This is useful
if your type contains a Foo<T>
that is Debug
even if T
is not.
Packed structures
You can use derivative to implement Debug
on packed structures. Unlike the standard derive(debug)
, derivative does not require the structure itself to be Copy
, but like the standard derive(debug)
, it requires each (non-ignored) field to be Copy
.
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Derivative)] #[derivative(Debug)] #[repr(C, packed)] struct Foo { foo: u8, // `String` isn't `Copy` so it must be ignored to derive `Debug` #[derivative(Debug="ignore")] bar: String, } }
Custom attributes
The Default
trait supports the following attributes:
- Container attributes
- Variant attributes
- Field attributes
Default enumeration
You can use derivative to derive a default implementation on enumerations!
This does not work with rustc's #[derive(Default)]
.
All you need is to specify what variant is the default value:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Debug, Derivative)] #[derivative(Default)] enum Enum { A, #[derivative(Default)] B, } println!("{:?}", Enum::default()); // B }
Setting the value of a field
You can use derivative to change the default value of a field in a Default
implementation:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Debug, Derivative)] #[derivative(Default)] struct Foo { foo: u8, #[derivative(Default(value="42"))] bar: u8, } println!("{:?}", Foo::default()); // Foo { foo: 0, bar: 42 } }
new
function
You can use derivative to derive a convenience new
method for your type
that calls Default::default
:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Debug, Derivative)] #[derivative(Default(new="true"))] struct Foo { foo: u8, bar: u8, } println!("{:?}", Foo::new()); // Foo { foo: 0, bar: 0 } }
Custom bound
The following does not work because derive
adds a T: Default
bound on the
impl Default for Foo<T>
:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Default)] struct Foo<T> { foo: Option<T>, } struct NonDefault; Foo::<NonDefault>::default(); // gives: // error: no associated item named `default` found for type `Foo<NonDefault>` in the current scope // = note: the method `default` exists but the following trait bounds were not satisfied: `NonDefault : std::default::Default` }
That bound however is useless as Option<T>: Default
for any T
.
derivative
allows you to explicitly specify a bound if the inferred one is not
correct:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Derivative)] #[derivative(Default(bound=""))] // don't need any bound struct Foo<T> { foo: Option<T>, } struct NonDefault; Foo::<NonDefault>::default(); // works! }
Custom attributes
The Hash
trait supports the following attributes:
- Container attributes
- Field attributes
Ignoring a field
You can use derivative to ignore fields from a Hash
implementation:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Derivative)] #[derivative(Hash)] struct Foo { foo: u8, #[derivative(Hash="ignore")] bar: i32, } #[derive(Hash)] struct Bar { foo: u8, } fn hash<T: std::hash::Hash>(t: &T) -> u64 { use std::hash::Hasher; let mut s = std::collections::hash_map::DefaultHasher::new(); t.hash(&mut s); s.finish() } assert_eq!(hash(&Foo { foo: 42, bar: -1337 }), hash(&Bar { foo: 42 })); }
Hash with
You can pass a field to a hash function:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; mod path { pub struct SomeTypeThatMightNotBeHash; pub mod to { pub fn my_hash_fn<H>(_: &super::SomeTypeThatMightNotBeHash, state: &mut H) where H: std::hash::Hasher { unimplemented!() } } } use path::SomeTypeThatMightNotBeHash; #[derive(Derivative)] #[derivative(Hash)] struct Foo { foo: u32, #[derivative(Hash(hash_with="path::to::my_hash_fn"))] bar: SomeTypeThatMightNotBeHash, } }
The field bar
will be hashed with path::to::my_hash_fn(&bar, &mut state)
where state
is the current Hasher
.
The function must the following prototype:
fn my_hash_fn<H>(&T, state: &mut H) where H: Hasher;
Limitations
On structure, derivative(Hash)
will produce the same hash as derive(Hash)
.
On unions however, it will produces the same hashes only for unitary
variants!
Custom bound
As most other traits, Hash
supports a custom bound on container and fields.
See Debug
's documentation for more information.
Custom attributes
The PartialEq
, Eq
, PartialOrd
and Eq
and traits support the following attributes:
- Container attributes
- Field attributes
The PartialEq
, PartialOrd
and Ord
traits also supports the following attributes:
- Container attributes
- Field attributes
(These attributes are not relevant for Eq
which is just a marker trait.)
Enumerations
Unfortunately, there is no way for derivative to derive PartialOrd
or Ord
on
enumerations as efficiently as the built-in derive(…)
yet.
If you want to use derivative on enumerations anyway, you can add
#[derivative(PartialOrd="feature_allow_slow_enum")]
to your enumeration. This acts as a “feature-gate”.
This attribute is also allowed for PartialEq
for historical reason. It is not
necessary anymore as of v2.1.0. It was never necessary nor allowed for Eq
.
Ignoring a field
You can use derivative to ignore a field when comparing:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Derivative)] #[derivative(PartialEq)] struct Foo { foo: u8, #[derivative(PartialEq="ignore")] bar: u8, } assert!(Foo { foo: 0, bar: 42 } == Foo { foo: 0, bar: 7}); assert!(Foo { foo: 42, bar: 0 } != Foo { foo: 7, bar: 0}); }
Compare with
Usually fields are compared using ==
, PartialOrd::partial_cmp
or Ord::cmp
. You can use an alternative comparison
function if you like:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; mod path { pub struct SomeTypeThatMightNotBePartialEq; pub mod to { pub fn my_cmp_fn(_: &super::SomeTypeThatMightNotBePartialEq, _: &super::SomeTypeThatMightNotBePartialEq) -> bool { false } } } use path::SomeTypeThatMightNotBePartialEq; #[derive(Derivative)] #[derivative(PartialEq)] struct Foo { foo: u32, #[derivative(PartialEq(compare_with="path::to::my_cmp_fn"))] bar: SomeTypeThatMightNotBePartialEq, } }
foo
will be compared with ==
and bar
will be compared with
path::to::my_cmp_fn
which must have the following prototype:
Trait | Signature |
---|---|
PartialEq | fn my_cmp_fn(&T, &T) -> bool; |
PartialOrd | fn my_cmp_fn(&T, &T) -> std::option::Option<std::cmp::Ordering>; |
Ord | fn my_cmp_fn(&T, &T) -> std::cmp::Ordering; |
Custom bound
Usually if you derive CmpTrait
, a T: CmpTrait
bound is added for each type parameter T
. You can use
override this behavior if the inferred bound is not correct for you.
Eg. comparing raw pointers does not require the type to be Eq
, so you could
use:
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Derivative)] #[derivative(PartialEq)] struct WithPtr<T: ?Sized> { #[derivative(PartialEq(bound=""))] foo: *const T } }
See Default
's documentation for more details.
Packed structures
You can use derivative to implement the comparison traits on packed structures. Unlike the standard derive
, derivative does not require the structure itself to be Copy
, but like the standard derive
, it requires each (non-ignored) field to be Copy
.
#![allow(unused)] fn main() { extern crate derivative; use derivative::Derivative; #[derive(Derivative)] #[derivative(PartialEq)] #[repr(C, packed)] struct Foo { f: u32, #[derivative(PartialEq = "ignore")] t: String, } }