derive_ex

Attribute Macro derive_ex 

Source
#[derive_ex]
Expand description

Improved version of the macro to implement the traits defined in the standard library.

§Attributes

You can write attributes in the following positions.

attributeimplstructenumvariantfield
#[derive_ex(Copy)]
#[derive_ex(Clone)]
#[derive_ex(Debug)]
#[derive_ex(Default)]
#[derive_ex(Ord)]
#[derive_ex(Deref)]
#[derive_ex(DerefMut)]
#[derive_ex(Add)]
#[derive_ex(AddAssign)]
#[derive_ex(Not)]
#[derive_ex(bound(...))]
#[derive_ex(dump))]
#[default]
#[debug]
#[ord]

§Derive Copy

You can use #[derive_ex(Copy)] to implement Copy.

use derive_ex::derive_ex;
use std::marker::PhantomData;

#[derive_ex(Copy, Clone)]
struct X<T>(PhantomData<T>);

The standard #[derive(Copy)] sets Copy constraint on the generic parameters, while #[derive_ex(Copy)] sets Copy constraint on the type of field containing generic parameters.

For example, adding #[derive(Copy)] to the previous structure X<T> generates the following code.

Since where T: Copy is specified, if T does not implement DebugCopy, then X<T> does not implement Copy.

use core::marker::Copy;
impl<T: Copy> Copy for X<T>
where
    T : Copy
{
}

On the other hand, #[derive_ex(Copy)] generates the following code.

Unlike the standard #[derive(Copy)], X<T> always implements Copy.

use core::marker::Copy;
impl<T: Copy> Copy for X<T>
where
    PhantomData<T>: Copy,
{
}

§Derive Clone

You can use #[derive_ex(Clone)] to implement Clone.

use derive_ex::derive_ex;

#[derive_ex(Clone)]
struct X(String);

#[derive_ex(Clone)]
enum Y {
  X,
  B,
}

It has the following differences from the standard #[derive(Clone)].

  • Generates Clone::clone_from.
  • The standard #[derive(Clone)] sets Clone constraint on the generic parameters, while #[derive_ex(Clone)] sets Clone constraint on the type of field containing generic parameters.

For example, to derive Clone for the following type

use std::rc::Rc;
struct X<T> {
    a: Rc<T>,
    b: String,
}

The standard #[derive(Clone)] generates the following code.

Since where T: Clone is specified, if T does not implement Clone, then X<T> does not implement Clone.

impl<T> Clone for X<T>
where
    T: Clone,
{
    fn clone(&self) -> Self {
        X {
            a: self.a.clone(),
            b: self.b.clone(),
        }
    }
}

On the other hand, #[derive_ex(Clone)] generates the following code.

Unlike the standard #[derive(Clone)], X<T> always implements Clone.

impl<T> Clone for X<T>
where
    Rc<T>: Clone,
{
    fn clone(&self) -> Self {
        X {
            a: self.a.clone(),
            b: self.b.clone(),
        }
    }
    fn clone_from(&mut self, source: &Self) {
        self.a.clone_from(&source.a);
        self.b.clone_from(&source.b);
    }
}

If you want to remove where Rc<T>: Clone in the above example, you can use #[derive(Clone(bound()))]. See Specify trait bound for details.

§Derive Debug

You can use #[derive_ex(Debug)] to implement Debug.

The following helper attribute arguments allow you to customize your Debug implementation.

attributestructenumvariantfield
ignore
transparent
bound(...)

§#[debug(ignore)]

By setting #[debug(ignore)] to a field, you can exclude that field from debug output.

use derive_ex::derive_ex;
#[derive_ex(Debug)]
struct X {
    a: u32,
    #[debug(ignore)]
    b: u32,
}
assert_eq!(format!("{:?}", X { a: 1, b: 2 }), "X { a: 1 }");

§#[debug(transparent)]

You can transfer processing to a field by setting #[debug(transparent)] to that field.

use derive_ex::derive_ex;
#[derive_ex(Debug)]
struct X {
    a: u32,
    #[debug(transparent)]
    b: u32,
}
assert_eq!(format!("{:?}", X { a: 1, b: 2 }), "2");

§#[debug(bound(...))]

The standard #[derive(Debug)] sets Debug constraint on the generic parameters, while #[derive_ex(Debug)] sets Debug constraint on the type of field containing generic parameters.

For example, to derive Debug for the following type

use std::marker::PhantomData;
struct X<T>(PhantomData<T>);

The standard #[derive(Debug)] generates the following code.

Since where T: Debug is specified, if T does not implement Debug, then X<T> does not implement Debug.

use std::fmt::Debug;
impl<T> Debug for X<T>
where
    T: Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("X").finish()
    }
}

On the other hand, #[derive_ex(Debug)] generates the following code.

Unlike the standard #[derive(Debug)], X<T> always implements Debug.

use std::fmt::Debug;
impl<T> Debug for X<T>
where
    PhantomData<T>: Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("X").finish()
    }
}

You can also manually specify a trait bound. See Specify trait bound for details.

§Derive Default

You can use #[derive_ex(Default)] to implement Default.

use derive_ex::derive_ex;
#[derive_ex(Default)]
struct X {
    a: u8,
}
let x = X::default();
assert_eq!(x.a, 0);

You can use #[default(...)] with struct or enum to set the default value.

use derive_ex::derive_ex;
#[derive_ex(Default)]
#[default(X::new())]
struct X(u8);

impl X {
    fn new() -> Self {
        X(5)
    }
}

let x = X::default();
assert_eq!(x.0, 5);

You can use #[default(...)] with a field to set the field’s default value.

use derive_ex::derive_ex;
#[derive_ex(Default)]
struct X {
    #[default(5)]
    a: u8,
}

let x = X::default();
assert_eq!(x.a, 5);

You can use #[default] to set the default variant.

use derive_ex::derive_ex;
#[derive(Debug, Eq, PartialEq)]
#[derive_ex(Default)]
enum X {
    A,
    #[default]
    B,
}
assert_eq!(X::default(), X::B);

If a string literal or a single path is specified in #[default(...)], conversion by Into is performed.

use derive_ex::derive_ex;

#[derive(Debug, PartialEq)]
#[derive_ex(Default)]
struct X(#[default("abc")]String);
assert_eq!(X::default(), X("abc".into()));

const S: &str = "xyz";
#[derive(Debug, PartialEq)]
#[derive_ex(Default)]
struct Y(#[default(S)]String);
assert_eq!(Y::default(), Y(S.into()));

The standard #[derive(Default)] sets Default constraint on the generic parameters, while #[derive_ex(Default)] sets Default constraint on the type of field containing generic parameters.

For example, to derive Default for the following type

struct X<T>(Option<T>);

The standard #[derive(Default)] generates the following code.

Since where T: Default is specified, if T does not implement Default, then X<T> does not implement Default.

impl<T> Default for X<T>
where
    T: Default,
{
    fn default() -> Self {
        X(<Option<T>>::default())
    }
}

On the other hand, #[derive_ex(Default)] generates the following code.

Unlike the standard #[derive(Default)], X<T> always implements Default.

impl<T> Default for X<T>
where
    Option<T>: Default,
{
    fn default() -> Self {
        X(<Option<T>>::default())
    }
}

Field types with manually set default values are not used as type constraints.

In the following example, where T : Default is not generated.

use derive_ex::derive_ex;

#[derive(Eq, PartialEq, Debug)]
struct NoDefault;
trait New {
    fn new() -> Self;
}
impl New for NoDefault {
    fn new() -> Self {
        NoDefault
    }
}

#[derive(Eq, PartialEq, Debug)]
#[derive_ex(Default)]
struct X<T: New> {
    #[default(T::new())]
    a: T,
}
assert_eq!(X::default(), X { a: NoDefault })

§Derive Ord, PartialOrd, Eq, PartialEq, Hash

Ord, PartialOrd, Eq, PartialEq, Hash can be derived using common settings.

To avoid repeating settings, one helper attribute affects multiple traits.

The table below shows which helper attributes affect which trait.

The helper attributes in the lines below are applied preferentially.

attributeOrdPartialOrdEqPartialEqHash
#[ord(...)]
#[partial_ord(...)]
#[eq(...)]
#[partial_eq(...)]
#[hash(...)]

The following table shows the helper attribute arguments and the locations where they can be used.

argumentstructenumvariantfield#[ord]#[partial_ord]#[eq]#[partial_eq]#[hash]
ignore
reverse
by = ...
key = ...
bound(...)

If you modify the behavior of a specific trait by by = ... or key = ..., you must also modify the behavior of other traits by by = ... or key = .... Trying to mix modified behavior with default behavior will result in a compilation error.

§#[ord(ignore)]

Specifying ignore for a field excludes that field from comparison and hash calculation.

You cannot change whether ignore is applied by the trait.

A compile error occurs when trying to apply ignore to only some of the traits.

use derive_ex::derive_ex;

#[derive_ex(Eq, PartialEq, Debug)]
struct X(#[eq(ignore)] u8);

assert_eq!(X(1), X(2));

§#[ord(reverse)]

Specifying reverse for a field reverses the comparison order.

It can also be used together with by = ... or key = ....

use derive_ex::derive_ex;

#[derive_ex(PartialOrd, PartialEq, Debug)]
struct X(#[partial_ord(reverse)] u8);

assert!(X(1) > X(2));

§#[ord(by = ...)]

You can set up a comparison method by specifying a function with the same signature as the trait’s required method.

#[hash(by = ...)] only changes the behavior of Hash.

Other attributes act on attributes other than Hash.

use derive_ex::derive_ex;
use std::cmp::Ordering;

#[derive_ex(Ord, PartialOrd, Eq, PartialEq, Debug)]
struct X(#[ord(by = f64::total_cmp)] f64);

assert_eq!(X(1.0).cmp(&X(2.0)), Ordering::Less);

§#[ord(key = ...)]

Delegates processing to the value of the specified expression.

In this expression, the field itself is represented by $.

use derive_ex::derive_ex;

#[derive_ex(Eq, PartialEq, Debug)]
struct X(#[eq(key = $.len())] &'static str);

assert_eq!(X("a"), X("b"));
assert_ne!(X("a"), X("aa"));

§#[ord(bound(...))]

Specify the trait bounds.

If the default trait bounds are specified with a high-priority helper attribute, the trait bounds of a lower-priority helper attribute will be used.

If by = ... or key = ... is specified with a high-priority helper attribute, the trait bounds of a lower-priority helper attribute will not be used.

For details, see Specify trait bound.

§Derive Deref

You can use #[derive(Deref)] for struct with a single field to implement Deref.

use derive_ex::derive_ex;

#[derive_ex(Deref)]
struct X(u8);

let _: &u8 = &X(10u8);

§Derive DerefMut

You can use #[derive(DerefMut)] for struct with a single field to implement DerefMut.

use derive_ex::derive_ex;

#[derive_ex(Deref, DerefMut)]
struct X(u8);

let _: &mut u8 = &mut X(10u8);

§Derive operators

§Add-like

In this document, Add is used as an example, but all of the following traits can be used in the same way.

  • Add
  • BitAnd
  • BitOr
  • BitXor
  • Div
  • Mul
  • Rem
  • Shl
  • Shr
  • Sub

§Derive Add from struct definition

You can use #[derive(Add)] on struct definition to derive Add.

use derive_ex::derive_ex;
#[derive_ex(Add)]
struct X {
    a: u8,
    b: u32,
}

The above code generates the following code.

impl Add<X> for X {
    type Output = X;
    fn add(self, rhs: X) -> Self::Output {
        X {
            a: self.a + rhs.a,
            b: self.b + rhs.b,
        }
    }
}
impl Add<&X> for X {
    type Output = X;
    fn add(self, rhs: &X) -> Self::Output {
        X {
            a: self.a + &rhs.a,
            b: self.b + &rhs.b,
        }
    }
}
impl Add<X> for &X {
    type Output = X;
    fn add(self, rhs: X) -> Self::Output {
        X {
            a: &self.a + rhs.a,
            b: &self.b + rhs.b,
        }
    }
}
impl Add<&X> for &X {
    type Output = X;
    fn add(self, rhs: &X) -> Self::Output {
        X {
            a: &self.a + &rhs.a,
            b: &self.b + &rhs.b,
        }
    }
}

§Derive Add from impl Add

By applying #[derive_ex(Add)] to one of the following, you can implement the remaining three.

  • impl Add<T> for T
  • impl Add<T> for &T
  • impl Add<&T> for T
  • impl Add<&T> for &T

T must implement Clone, except when #[derive_ex(Add)] is applied to impl Add<&T> for &T.

use derive_ex::derive_ex;
use std::ops::Add;

#[derive(Clone)]
struct A {
    a: u8,
}

#[derive_ex(Add)]
impl Add<A> for A {
    type Output = A;
    fn add(self, rhs: A) -> Self::Output {
        A { a: self.a + rhs.a }
    }
}

The above code generates the following code.

impl Add<&A> for A {
    type Output = A;
    fn add(self, rhs: &A) -> Self::Output {
        self + rhs.clone()
    }
}
impl Add<A> for &A {
    type Output = A;
    fn add(self, rhs: A) -> Self::Output {
        self.clone() + rhs
    }
}
impl Add<&A> for &A {
    type Output = A;
    fn add(self, rhs: &A) -> Self::Output {
        self.clone() + rhs.clone()
    }
}

§Derive Add from impl AddAssign

By applying #[derive_ex(Add)] to impl AddAssign<Rhs> for T, you can implement Add<Rhs> for T.

use derive_ex::derive_ex;
use std::ops::AddAssign;

struct X {
    a: u8,
    b: u32,
}
#[derive_ex(Add)]
impl std::ops::AddAssign<u8> for X {
    fn add_assign(&mut self, rhs: u8) {
        self.a += rhs;
        self.b += rhs as u32;
    }
}

The above code generates the following code.

use std::ops::Add;

impl Add<u8> for X {
    type Output = X;
    fn add(mut self, rhs: u8) -> Self::Output {
        self += rhs;
        self
    }
}

§AddAssign-like

In this document, AddAssign is used as an example, but all of the following traits can be used in the same way.

  • AddAssign
  • BitAndAssign
  • BitOrAssign
  • BitXorAssign
  • DivAssign
  • MulAssign
  • RemAssign
  • ShlAssign
  • ShrAssign
  • SubAssign

§Derive AddAssign from struct definition

You can use #[derive(AddAssign)] on struct definition to derive Assign.

use derive_ex::derive_ex;

#[derive_ex(AddAssign)]
struct X {
    a: u8,
    b: u32,
}

The above code generates the following code.

use std::ops::AddAssign;

impl AddAssign<X> for X {
    fn add_assign(&mut self, rhs: X) {
        self.a += rhs.a;
        self.b += rhs.b;
    }
}
impl AddAssign<&X> for X {
    fn add_assign(&mut self, rhs: &X) {
        self.a += &rhs.a;
        self.b += &rhs.b;
    }
}

§Derive AddAssign from impl Add

By applying #[derive_ex(AddAssign)] to impl Add<Rhs> for T or impl Add<Rhs> for &T, you can implement AddAssign<Rhs> for T.

When #[derive_ex(AddAssign)] is applied to impl Add<Rhs> for &T, T must implement Clone.

When #[derive_ex(AddAssign)] is applied to impl Add<Rhs> for T, T does not have to implement Clone.

use derive_ex::derive_ex;
use std::ops::Add;

#[derive(Clone)]
struct X {
    a: u8,
    b: u32,
}

#[derive_ex(AddAssign)]
impl Add<u8> for X {
    type Output = X;
    fn add(mut self, rhs: u8) -> Self::Output {
        self.a += rhs;
        self.b += rhs as u32;
        self
    }
}
use std::ops::AddAssign;

impl AddAssign<u8> for X {
    fn add_assign(&mut self, rhs: u8) {
        *self = self.clone() + rhs;
    }
}

§Derive both Add and AddAssign from impl Add

Applying #[derive_ex(Add, AddAssign)] to one of the four below will implement the other three, plus generate the same code as when #[derive_ex(AddAssign)] is applied to impl Add<T> for &T and impl Add<&T> for &T.

  • impl Add<T> for T
  • impl Add<T> for &T
  • impl Add<&T> for T
  • impl Add<&T> for &T

§Not-like

In this document, Not is used as an example, but all of the following traits can be used in the same way.

  • Not
  • Neg

You can use #[derive_ex(Not)] to implement Not.

use derive_ex::derive_ex;

#[derive_ex(Not)]
struct X {
    a: bool,
    b: bool,
}

The above code generates the following code.

use std::ops::Not;

impl Not for X {
    type Output = X;
    fn not(self) -> Self::Output {
        X {
            a: !self.a,
            b: !self.b,
        }
    }
}
impl Not for &X {
    type Output = X;
    fn not(self) -> Self::Output {
        X {
            a: !&self.a,
            b: !&self.b,
        }
    }
}

§Specify trait bound

If the type definition or impl item to which #[derive_ex] is applied has generic parameters, then by default, trait bound required by the auto-generated code is set.

You can change the generated trait bound by specifying bound(...) in attribute.

bound(...) can be used in the following places, the lower the number, the higher the priority.

struct, enumvariantfield
#[trait_name(bound(...))]147
#[derive_ex(TraitName(bound(...)))]258
#[derive_ex(TraitName, bound(...))]369
use derive_ex::derive_ex;

#[derive_ex(Default(bound(T)/* <-- 2 */), bound(T)/* <-- 3 */)]
#[default(_, bound(T))] // <-- 1
enum X<T> {
    A,
    #[derive_ex(Default(bound(T)/* <-- 5 */), bound(T)/* <-- 6 */)]
    #[default(_, bound(T))] // <-- 4
    B {
        #[derive_ex(Default(bound(T)/* <-- 8 */), bound(T)/* <-- 9 */)]
        #[default(_, bound(T))] // <-- 7
        t: T,
    },
}

In 3, 6, and 9, common trait bound can be set for multiple traits by using it as like #[derive_ex(Clone, Default, bound(T))].

Trait bound can be specified in the following three ways.

  • Type (T)
  • Predicate (T : TraitName)
  • Default (..)

§#[bound(T)]

If you specify a type in #[bound(...)], make sure that the specified type implements the trait to be generated.

use derive_ex::derive_ex;

#[derive_ex(Default, Clone, bound(T))]
struct X<T>(Box<T>);

The above code generates the following code.

By bound(T), Box<T> : Default is changed to T : Default and Box<T> : Clone is changed to T : Clone.

impl<T> Default for X<T>
where
    T: Default,
{
    fn default() -> Self {
        X(Box::default())
    }
}

impl<T> Clone for X<T>
where
    T: Clone,
{
    fn clone(&self) -> Self {
        X(self.0.clone())
    }
    fn clone_from(&mut self, source: &Self) {
        self.0.clone_from(&source.0)
    }
}

§#[bound(T : TraitName)]

If a predicate is specified, it is used as-is.

use derive_ex::derive_ex;

#[derive_ex(Clone, bound(T : Clone + Copy))]
struct X<T>(T);

The above code generates the following code.

By bound(T : Clone + Copy), T : Clone is changed to T : Clone + Copy.

impl<T> Clone for X<T>
where
    T: Clone + Copy,
{
    fn clone(&self) -> Self {
        X(self.0.clone())
    }
    fn clone_from(&mut self, source: &Self) {
        self.0.clone_from(&source.0)
    }
}

§#[bound(..)]

.. means default trait bound.

If .. is specified, the lower priority trait bound is used.

use derive_ex::derive_ex;

#[derive_ex(Clone, bound(T : Copy, ..))]
struct X<T>(T);

The above code generates the following code.

By bound(T : Copy, ..) ,T : Copy is added to T : Clone which is the default trait bound.

impl<T> Clone for X<T>
where
    T: Clone,
    T: Copy,
{
    fn clone(&self) -> Self {
        X(self.0.clone())
    }
    fn clone_from(&mut self, source: &Self) {
        self.0.clone_from(&source.0)
    }
}

§#[bound()]

An empty argument means no constraint.

use derive_ex::derive_ex;
use std::rc::Rc;

#[derive_ex(Clone, bound())]
struct X<T>(Rc<T>);

The above code generates the following code.

By bound(), where Rc<T> : Clone is removed.

impl<T> Clone for X<T> {
    fn clone(&self) -> Self {
        X(self.0.clone())
    }
    fn clone_from(&mut self, source: &Self) {
        self.0.clone_from(&source.0)
    }
}

§Display generated code

The generated code is output as an error message by using #[derive_ex(dump)].

#[derive_ex(Clone, dump)]
struct X<T>(T);

The above code causes the following error.

error: dump:
       impl < T > :: core :: clone :: Clone for X < T > where T : :: core :: clone ::
       Clone,
       {
           fn clone(& self) -> Self
           { X(< T as :: core :: clone :: Clone > :: clone(& self.0),) } fn
           clone_from(& mut self, source : & Self)
           {
               < T as :: core :: clone :: Clone > ::
               clone_from(& mut self.0, & source.0) ;
           }
       }

As with bound(...), dump can be applied to multiple traits by writing #[derive_ex(Clone, Default, dump)].