derive_more

repository·master·Indexed 24 days ago

https://github.com/jeltef/derive_more

A Rust library providing procedural macros to automatically derive commonly used traits for structs and enums, such as conversion, formatting, and operators. Version 2.1.1 reduces boilerplate by implementing traits like From, Add, Into, Display, AsRef, and AsMut through #[derive(x)] macros.

Tokens
28.7K
Snippets
74
Records
129
Agent score
83%

What's inside derive_more

  1. Derive the `Deref` trait

    master

    The #[derive(Deref)] macro allows you to automatically implement the std::ops::Deref trait for structs or enums. This is useful for making a type behave like a reference to one of its internal fields.

    Constraints:

    • For structs: You can only derive Deref for a single field.
    • For enums: You can only derive Deref for a single field per variant.

    Two modes of operation:

    1. Dereferencing to a field: The type acts as if it were a reference type, returning a reference to the specified field.
    2. Forwarding dereference: Used when the field itself is a reference type (like Box<T> or &T). The implementation calls the deref method of the field's type.

    Use the #[deref] attribute to specify which field to use, and #[deref(forward)] on the container to enable forwarding mode.

    use derive_more::Deref;
    
    #[derive(Deref)]
    struct Num {
        num: i32,
    }
    
    #[derive(Deref)]
    #[deref(forward)]
    struct MyBoxedInt(Box<i32>);
    
    let num = Num{num: 123};
    let boxed = MyBoxedInt(Box::new(123));
    assert_eq!(123, *num);
    assert_eq!(123, *boxed);
  2. Use `#[derive(Into)]` to extract values from structs

    master

    The #[derive(Into)] macro allows you to extract the values contained within a struct. It does not implement the Into trait directly; instead, it derives From for the contained types, providing an indirect implementation of Into as recommended by Rust documentation.

    Single-field structs

    For structs with one field, calling .into() returns the inner type.

    Multi-field structs

    For structs with multiple fields, calling .into() returns a tuple containing the values of those fields.

    Customizing conversions with #[into(<types>)]

    You can use the #[into(<types>)] attribute on a struct to specify concrete types for the conversions. This is useful for mapping fields to specific types like Cow, String, or specific tuple shapes (e.g., (i64, i64) vs (i32, i32)).

    Reference conversions

    You can derive conversions into references (mutable or immutable) using #[into(ref(...))] or #[into(ref_mut(...))] within the struct attribute.

    Skipping fields

    To exclude specific fields from the conversion, use the #[into(skip)] or #[into(ignore)] attribute on the field.

    #[derive(Debug, Into, PartialEq)]
    struct Int(i32);
    
    assert_eq!(2, Int(2).into());
  3. Use structural `#[derive(Mul)]` with `#[mul(forward)]`

    master

    By applying the #[mul(forward)] attribute, you can implement structural multiplication. This behaves like Add: it multiplies corresponding fields of two instances of the same type together.

    Structs

    For structs, it multiplies each field of the left-hand side by the corresponding field of the right-hand side. Fields can be excluded using #[mul(skip)] or #[mul(ignore)].

    Enums

    For enums, structural multiplication works variant-to-variant.

    • Success: If both instances are the same variant, it multiplies their fields and returns Ok(Self).
    • Failure: If the variants do not match, it returns Err(derive_more::BinaryError::Mismatch).
    • Unit Variants: Multiplying Unit variants together returns a derive_more::BinaryError::Unit error.
    • Return Type: The Output type for enum structural Mul is Result<Self, derive_more::BinaryError>.
    use derive_more::Mul;
    use core::marker::PhantomData;
    
    #[derive(Mul)]
    #[mul(forward)]
    struct Point2D {
        x: i32,
        y: i32,
    }
    
    #[derive(Mul)]
    #[mul(forward)]
    enum MixedInts {
        BigInt(i64),
        NamedSmallInts { x: i32, y: i32 },
        Unit,
    }
    
    #[derive(Mul)]
    #[mul(forward)]
    enum MixedIntsWithSkip {
        TwoSmallInts(i32, #[mul(skip)] i32),
        NamedSmallInts {
            #[mul(skip)]
            x: i32,
            y: i32,
        },
    }
  4. Use `#[derive(Hash)]` for structural hashing

    master

    Deriving Hash with derive_more performs structural hashing by hashing values according to their type structure. While it behaves similarly to std for enums and structs (hashing all available fields), it provides three key advantages:

    1. Reduced constraints: It does not overconstrain generic parameters.
    2. Field/Variant skipping: You can ignore specific fields, entire structs, or enum variants using the #[hash(skip)] attribute.
    3. Custom hashing: You can specify a custom hash function for individual fields using the #[hash(with(function))] attribute.

    For enums, the implementation hashes the discriminant first, followed by the fields of the active variant.

    use derive_more::Hash;
    
    #[derive(Hash)]
    struct Foo<A, B, C> {
        a: A,
        b: B,
        c: C,
    }
  5. Use `#[deref(forward)]` and `#[deref_mut(forward)]` for wrapper types

    master

    When creating a wrapper type (like a newtype pattern) around a type that already implements Deref and DerefMut (e.g., Box<T>), use the forward attribute. This tells derive_more to delegate the deref and deref_mut calls to the inner field's own implementations rather than just returning a reference to the field itself.

    This is common for tuple structs like struct MyBoxedInt(Box<i32>);.

    #[derive(Deref, DerefMut)]
    #[deref(forward)]
    #[deref_mut(forward)]
    struct MyBoxedInt(Box<i32>);
  6. How `#[derive(DerefMut)]` works

    master

    The DerefMut derive macro implements the core::ops::DerefMut trait for your type. This allows you to mutably dereference a struct or enum variant to access its underlying member directly.

    Key Requirements & Constraints:

    • Single Field: Deriving Deref or DerefMut only works if the type has a single field, or if each variant of an enum has exactly one field designated for dereferencing.
    • Dependency on Deref: Because DerefMut requires the target type to also implement Deref, you should almost always derive both Deref and DerefMut together.
    • Two Dereference Modes:
      1. Direct Dereferencing: Accessing the field directly (treating the struct as if it were the field type).
      2. Double Dereferencing: When the field itself is a reference type (like Box or &mut), the macro handles the extra layer of dereferencing.

    Use #[deref_mut] or #[deref_mut(ignore)] to specify which field should be used for the implementation.

  7. How TryUnwrap error handling works

    master

    When using #[derive(TryUnwrap)], if a call to a try_unwrap_* method fails because the enum is not the expected variant, it returns a TryUnwrapError<T>.

    Crucially, the TryUnwrapError contains the original input value (the input field). This prevents the value from being dropped during a failed conversion attempt, allowing the caller to recover the data.

    Example error structure behavior:

    • err.input: The original enum instance that failed to unwrap.
    • err.to_string(): A descriptive error message (e.g., "Attempt to call Maybe::try_unwrap_just()on aMaybe::Nothing value").
  8. Handle generic type bounds in `Display` derives

    master

    When deriving Display for generic types, derive_more automatically infers trait bounds for generic arguments used directly in interpolation. For example, if a field a: T1 is used in #[display("{a}")], T1: Display is automatically added as a bound.

    Custom Trait Bounds: If you use complex expressions in the format string (e.g., a.my_function()), derive_more cannot infer the necessary bounds. You must specify them explicitly using the #[display(bound(...))] attribute.

    use derive_more::with_trait::Display;
    
    trait MyTrait { fn my_function(&self) -> i32; }
    
    #[derive(Display)]
    #[display(bound(T: MyTrait, U: Display))]
    #[display("{} {} {}", a.my_function(), b.to_string().len(), c)]
    struct MyStruct<T, U, V> {
        a: T,
        b: U,
        c: V,
    }
  9. Configure how `source()` is derived

    master

    The source() method returns the underlying cause of an error. #[derive(Error)] determines which field to return as the source using the following logic:

    1. Named Field: If a struct or enum variant has a field named source, it is returned.
    2. Tuple Struct/Variant: If there is exactly one field that is not being used as a backtrace (e.g., a single-field tuple, or a two-field tuple where one is the backtrace), that field is returned.
    3. Explicit Attribute: A field annotated with #[error(source)] is returned.

    Handling Optional Sources:

    • source() naturally supports Option<_> types.
    • If you use a custom type or a renamed Option (e.g., type MyOption<T> = Option<T>), you must explicitly mark it using #[error(source(optional))] so the derive macro recognizes it as an optional source.
  10. Limitations of `IntoIterator` derivation

    master

    When using #[derive(IntoIterator)], keep the following constraints in mind:

    1. Single Field Only: Deriving IntoIterator only works for a single field of a struct. You must use the #[into_iterator] attribute to identify that field.
    2. No Enum Support: Deriving IntoIterator is not supported for enums.