Merge

Derive Macro Merge 

Source
#[derive(Merge)]
{
    // Attributes available to this derive:
    #[partial]
    #[strategy]
}
Expand description

Automatically implements Merge for a given structure.

This derive will create a near-identical partial version of the structure with each field being optional. For example:

// Deriving `Merge` on this struct...
#[derive(Merge)]
#[partial(PartialConfig)]
struct Config {
    name: String,
    version: u32,
    dependencies: Vec<String>,
}
// ...will create this struct as well.
struct PartialConfig {
    name: Option<String>,
    version: Option<u32>,
    dependencies: Option<Vec<String>>,
}

§Attributes

  • #[partial(Name)], #[partial(Name, ...)] (struct)

    What: This specifies the name of the partial struct generated, as well as any attributes that should be applied to the partial struct.

    Where: This should annotate the struct itself.

    How: The name should be a single identifier inside the parenthesis, and is commonly prefixed with “Partial”. Attributes to be applied to the partial struct may optionally be specified after name, separated by commas.

    Required

  • #[partial(...)] (field)

    What: This specifies attributes that should annotate fields within the partial struct.

    Where: This should annotate fields within the struct.

    How: This accepts a comma-separated list of attributes to be applied to the partial’s field inside the parenthesis. At least one attribute is required.

    Optional

  • #[strategy(overwrite | merge)] (field)

    What: This specifies how this field should be merged.

    Where: This should annotate the struct’s fields.

    How: The value should either be overwrite or merge in parenthesis. overwrite will replace the base’s field with the partial’s if it exists, while merge will use the field type’s Merge implementation to combine the two values together.

    Optional: Fields without this attribute default to overwrite.

§Examples

#[derive(Merge)]
// Name the partial version of this type `PartialConfig`.
#[partial(PartialConfig)]
struct Config {
    // This field will be overwritten when merged. `#[strategy(overwrite)]` may be omitted.
    #[strategy(overwrite)]
    name: String,

    // This field will also be overwritten when merged.
    version: u32,

    // This field will be combined when merged.
    #[strategy(merge)]
    dependencies: Vec<String>,
}

Struct and field attributes can be applied to the partial struct using the #[partial(...)] attribute. This is commonly used to implement Default for the partial struct, as its fields are all Option<T>s.

#[derive(Merge)]
// Implement `Default` for the partial struct.
#[partial(PartialFish, derive(Default))]
struct Fish {
    fins: u8,
    memory: f32,
    leashed: bool,
}
#[derive(Merge)]
// Implement `Deserialize` for the partial struct, denying unknown fields.
#[partial(PartialConfig, derive(Deserialize), serde(deny_unknown_fields))]
struct Config {
    name: String,

    // When deserializing the partial struct, accept the value of either "version" or "v" for
    // this field.
    #[partial(serde(alias = "v"))]
    version: u32,
}

Be warned that the fields of partial structs are all Option<T>s. This may make certain attributes like #[serde(default)] behave differently.

#[derive(Merge)]
#[partial(PartialTrickyDefault, derive(Deserialize))]
struct TrickyDefault {
    // This attribute actually gets applied to a field like `value: Option<u64>`. Because of
    // this, the default value will be `None` and not 0.
    #[partial(serde(default))]
    tricky_value: u64,

    // This fixes the issue by making the partial field default to `Some(0)`. Much better!
    #[partial(serde(default = "zero_default"))]
    corrected_value: u64,
}

fn zero_default() -> Option<u64> {
    Some(0)
}

Simple generics are supported, however only generic types that can merge with themselves can be annotated with #[strategy(merge)].

#[derive(Merge)]
#[partial(PartialNamedData)]
struct NamedData<T> {
    name: String,
    data: T,
}
#[derive(Merge)]
#[partial(PartialNamedData)]
// `T: Merge<T>` means any type that can be merged with itself.
struct NamedData<T: Merge<T>>
{
    name: String,
    #[strategy(merge)]
    data: T,
}

Unit structs can also derive Merge, however there is little point in doing so.

#[derive(Merge)]
#[partial(PartialConfig)]
struct Config;

§Errors

This macro only works on named structs. Enums, unions, or tuple structs will not compile.

#[derive(Merge)]
#[partial(PartialChoice)]
enum Choice {
    A,
    B,
}
#[derive(Merge)]
#[partial(PartialConfig)]
struct Config(bool, u8, Vec<String>);

This macro requires a single #[partial(...)] attribute on the struct itself.

#[derive(Merge)]
// Missing `#[partial(...)]`.
struct Config {
    name: String,
    dependencies: Vec<String>,
}
#[derive(Merge)]
// Too many `#[partial(...)]`s.
#[partial(PartialConfig1)]
#[partial(PartialConfig2)]
struct Config {
    name: String,
    dependencies: Vec<String>,
}

This macro only supports the overwrite and merge strategies.

#[derive(Merge)]
#[partial(PartialDog)]
struct Dog {
    name: String,
    // `add` is not a valid strategy.
    #[strategy(add)]
    age: u16,
}