#[duplicate_item]
Expand description

Duplicates the item and substitutes specific identifiers for different code snippets in each duplicate.

§Short Syntax

use duplicate::duplicate_item;
trait IsMax {
  fn is_max(&self) -> bool;
}

#[duplicate_item(
  int_type  max_value;
  [ u8 ]    [ 255 ];
  [ u16 ]   [ 65_535 ];
  [ u32 ]   [ 4_294_967_295 ];
)]
impl IsMax for int_type {
  fn is_max(&self) -> bool {
    *self == max_value
  }
}

assert!(!42u8.is_max());
assert!(!42u16.is_max());
assert!(!42u32.is_max());

The implementation of IsMax is duplicated 3 times:

  1. For the type u8 and the its maximum value 255.
  2. For the type u16 and the its maximum value 65_535 .
  3. For the type u32 and the its maximum value 4_294_967_295 .

This syntax must start with a list of all identifiers followed by ;. Then a ; seperated list of substitution groups must be given (at least 1 group). Every group is a list of substitutions, one for each substitution identifier given in the first line. The substitutions must be enclosed in [] but are otherwise free.

§Verbose Syntax

use duplicate::duplicate_item;
trait IsMax {
  fn is_max(&self) -> bool;
}

#[duplicate_item(
  [
    int_type  [ u8 ]
    max_value [ 255 ]
  ]
  [
    int_type  [ u16 ]
    max_value [ 65_535 ]
  ]
  [
    max_value [ 4_294_967_295 ]
    int_type  [ u32 ]
  ]
)]
impl IsMax for int_type {
  fn is_max(&self) -> bool {
    *self == max_value
  }
}

assert!(!42u8.is_max());
assert!(!42u16.is_max());
assert!(!42u32.is_max());

Has the same functionality as the previous short-syntax example.

For each duplicate needed, a substitution group must be given enclosed in []. A substitution group is a set of identifiers and substitution pairs, like in the short syntax, but there can only be one substitution per identifier. All substitution groups must have the same identifiers, however their order is unimportant, as can be seen from the last substitution group above, where max_value comes before int_type.

§Parameterized Substitutoin

use duplicate::duplicate_item;
struct VecWrap<T>(Vec<T>);

impl<T> VecWrap<T> {
  #[duplicate_item(
    method     reference(lifetime, type);
    [get]      [& 'lifetime type];
    [get_mut]  [& 'lifetime mut type];
  )]
  pub fn method<'a>(self: reference([a],[Self]),idx: usize) -> Option<reference([a],[T])> {
    self.0.method(idx)
  }
}

let mut vec = VecWrap(vec![1,2,3]);
assert_eq!(*vec.get(0).unwrap(), 1);
*vec.get_mut(1).unwrap() = 5;
assert_eq!(*vec.get(1).unwrap(), 5);

This implements two versions of the method:

  • get: Borrows self immutably and return a shared reference.
  • get_mut: Borrows self mutably and returns a mutable reference.

If an identifier is followed by parenthises (in both its declaration and its use), a set of parameters can be provided to customize the subtituion for each use. In the declaration a list of identifiers is given, which can be used in its substitutions. When using the identifier, argument code snippets must be given in a comma separated list, with each argument being inclosed in [].

Parameterized substitution is also available for the verbose syntax:

impl<T> VecWrap<T> {
  #[duplicate_item(
    [
      method                     [get]
      reference(lifetime, type)  [& 'lifetime type]
    ]
    [
      method                     [get_mut]
      reference(lifetime, type)  [& 'lifetime mut type]
    ]
  )]
  pub fn method<'a>(self: reference([a],[Self]),idx: usize) -> Option<reference([a],[T])> {
    self.0.method(idx)
  }
}

§Nested Invocation

use duplicate::duplicate_item;
trait IsNegative {
  fn is_negative(&self) -> bool;
}

#[duplicate_item(
  int_type implementation;
  duplicate!{
    [                                  // -+
      int_type_nested;[u8];[u16];[u32] //  | Nested invocation producing 3
    ]                                  //  | substitution groups
    [ int_type_nested ] [ false ];     //  |
  }                                    // -+
  [ i8 ] [ *self < 0 ]                 // -- Substitution group 4
)]
impl IsNegative for int_type {
  fn is_negative(&self) -> bool {
    implementation
  }
}

assert!(!42u8.is_negative());
assert!(!42u16.is_negative());
assert!(!42u32.is_negative());
assert!(!42i8.is_negative());

This implements IsNegative 4 times:

  1. For the type u8 with the implementation of the method simply returning false. 1. For the type u16 the same way as u8.
  2. For the type u32 the same way as u8 and u16.
  3. For i8 with the implementation of the method checking whether it’s less than 0.

We used # to start a nested invocation of the macro. In it, we use the identifier int_type_nested to substitute the 3 unsigned integer types into the body of the nested invocation, which is a substitution group for the outer macro invocation. This therefore produces the three substitution groups that makes the outer macro make the duplicates for the unsigned integers.

This code is identical to the following, which doesn’t use nested invocation:

#[duplicate_item(
  int_type implementation;
  [ u8 ]  [ false ];
  [ u16 ] [ false ];
  [ u32 ] [ false ];
  [ i8 ]  [ *self < 0 ]
)]
impl IsNegative for int_type {
  fn is_negative(&self) -> bool {
    implementation
  }
}

Nested invocation is also available for the verbose syntax:

use duplicate::duplicate_item;
trait IsNegative {
  fn is_negative(&self) -> bool;
}

#[duplicate_item(
  duplicate!{                            // -+
    [ int_type_nested;[u8];[u16];[u32] ] //  |
    [                                    //  | Nested invocation producing 3
      int_type [ int_type_nested ]       //  | substitution groups
      implementation [ false ]           //  |
    ]                                    //  |
  }                                      // -+
  [                                      // -+
    int_type [ i8 ]                      //  | Substitution group 4
    implementation [ *self < 0 ]         //  |
  ]                                      // -+
)]
impl IsNegative for int_type {
  fn is_negative(&self) -> bool {
    implementation
  }
}

assert!(!42u8.is_negative());
assert!(!42u16.is_negative());
assert!(!42u32.is_negative());
assert!(!42i8.is_negative());

§Global Substitution

#[duplicate_item(
  typ1 [Some<Complex<()>, Type<WeDont<Want, To, Repeat>>>];
  typ2 [Some<Other, Complex<Type<(To, Repeat)>>>];
  method     reference(type);
  [get]      [& type];
  [get_mut]  [&mut type];
)]
fn method(
  arg0: reference([Type<()>]),
  arg1: typ1,
  arg2: typ2)
  -> (reference([typ1]), reference([typ2]))
{
  ...
}

The global substitutions (typ1 and typ2) are substituted in both duplicates of the function. Global substitutions have the same syntax as verbose syntax substitutions, are ; separated (even from following substitutions groups), must all be defined at the beginning, and aren’t usable in the invocation itself but only in the code being duplicated.