Выбрать главу

($left:expr; and $right:expr) => {

println!("{:?} and {:?} is {:?}",

stringify!($left),

stringify!($right),

$left && $right)

};

// ^ each arm must end with a semicolon.

($left:expr; or $right:expr) => {

println!("{:?} or {:?} is {:?}",

stringify!($left),

stringify!($right),

$left || $right)

};

}

fn main() {

test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);

test!(true; or false);

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Macros can use + in the argument list to indicate that an argument may repeat at least once, or *, to indicate that the argument may repeat zero or more times.

In the following example, surrounding the matcher with $(...),+ will match one or more expression, separated by commas. Also note that the semicolon is optional on the last case.

// `find_min!` will calculate the minimum of any number of arguments.

macro_rules! find_min {

// Base case:

($x:expr) => ($x);

// `$x` followed by at least one `$y,`

($x:expr, $($y:expr),+) => (

// Call `find_min!` on the tail `$y`

std::cmp::min($x, find_min!($($y),+))

)

}

fn main() {

println!("{}", find_min!(1u32));

println!("{}", find_min!(1u32 + 2, 2u32));

println!("{}", find_min!(5u32, 2u32 * 3, 4u32));

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

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>:

use std::ops::{Add, Mul, Sub};

macro_rules! assert_equal_len {

// The `tt` (token tree) designator is used for

// operators and tokens.

($a:expr, $b:expr, $func:ident, $op:tt) => {

assert!($a.len() == $b.len(),

"{:?}: dimension mismatch: {:?} {:?} {:?}",

stringify!($func),

($a.len(),),

stringify!($op),

($b.len(),));

};

}

macro_rules! op {

($func:ident, $bound:ident, $op:tt, $method:ident) => {

fn $func<T: $bound<T, Output=T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {

assert_equal_len!(xs, ys, $func, $op);

for (x, y) in xs.iter_mut().zip(ys.iter()) {

*x = $bound::$method(*x, *y);

// *x = x.$method(*y);

}

}

};

}

// Implement `add_assign`, `mul_assign`, and `sub_assign` functions.

op!(add_assign, Add, +=, add);

op!(mul_assign, Mul, *=, mul);

op!(sub_assign, Sub, -=, sub);

mod test {

use std::iter;

macro_rules! test {

($func:ident, $x:expr, $y:expr, $z:expr) => {

#[test]

fn $func() {

for size in 0usize..10 {

let mut x: Vec<_> = iter::repeat($x).take(size).collect();

let y: Vec<_> = iter::repeat($y).take(size).collect();

let z: Vec<_> = iter::repeat($z).take(size).collect();

super::$func(&mut x, &y);

assert_eq!(x, z);

}

}

};

}

// Test `add_assign`, `mul_assign`, and `sub_assign`.

test!(add_assign, 1u32, 2u32, 3u32);

test!(mul_assign, 2u32, 3u32, 6u32);

test!(sub_assign, 3u32, 2u32, 1u32);

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

$ rustc --test dry.rs && ./dry

running 3 tests

test test::mul_assign ... ok

test test::add_assign ... ok

test test::sub_assign ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured

A DSL is a mini "language" embedded in a Rust macro. It is completely valid Rust because the macro system expands into normal Rust constructs, but it looks like a small language. This allows you to define concise or intuitive syntax for some special functionality (within bounds).