DRY (Don’t Repeat Yourself)

Macros allow writing DRY code by factoring out the common parts of functions
and/or test suites. Here is an example that implements and tests the +=, *=
and -= operators on Vec<T>:

  1. use std::ops::{Add, Mul, Sub};
  2. macro_rules! assert_equal_len {
  3. // The `tt` (token tree) designator is used for
  4. // operators and tokens.
  5. ($a:ident, $b: ident, $func:ident, $op:tt) => (
  6. assert!($a.len() == $b.len(),
  7. "{:?}: dimension mismatch: {:?} {:?} {:?}",
  8. stringify!($func),
  9. ($a.len(),),
  10. stringify!($op),
  11. ($b.len(),));
  12. )
  13. }
  14. macro_rules! op {
  15. ($func:ident, $bound:ident, $op:tt, $method:ident) => (
  16. fn $func<T: $bound<T, Output=T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {
  17. assert_equal_len!(xs, ys, $func, $op);
  18. for (x, y) in xs.iter_mut().zip(ys.iter()) {
  19. *x = $bound::$method(*x, *y);
  20. // *x = x.$method(*y);
  21. }
  22. }
  23. )
  24. }
  25. // Implement `add_assign`, `mul_assign`, and `sub_assign` functions.
  26. op!(add_assign, Add, +=, add);
  27. op!(mul_assign, Mul, *=, mul);
  28. op!(sub_assign, Sub, -=, sub);
  29. mod test {
  30. use std::iter;
  31. macro_rules! test {
  32. ($func: ident, $x:expr, $y:expr, $z:expr) => {
  33. #[test]
  34. fn $func() {
  35. for size in 0usize..10 {
  36. let mut x: Vec<_> = iter::repeat($x).take(size).collect();
  37. let y: Vec<_> = iter::repeat($y).take(size).collect();
  38. let z: Vec<_> = iter::repeat($z).take(size).collect();
  39. super::$func(&mut x, &y);
  40. assert_eq!(x, z);
  41. }
  42. }
  43. }
  44. }
  45. // Test `add_assign`, `mul_assign` and `sub_assign`
  46. test!(add_assign, 1u32, 2u32, 3u32);
  47. test!(mul_assign, 2u32, 3u32, 6u32);
  48. test!(sub_assign, 3u32, 2u32, 1u32);
  49. }
  1. $ rustc --test dry.rs && ./dry
  2. running 3 tests
  3. test test::mul_assign ... ok
  4. test test::add_assign ... ok
  5. test test::sub_assign ... ok
  6. test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured