Rust на примерах — страница 42 из 65

   • Шаблоны и указатели

   • Перегрузка

   • Повторение

Указатели

Аргументы макроса имеют префикс знака доллара $ и тип аннотируется с помощью указателей фрагмента:

macro_rules! create_function {

// Этот макрос принимает аргумент идентификатора `ident` и

// создаёт функцию с именем `$func_name`.

// Идентификатор `ident` используют для обозначения имени переменной/функции.

($func_name:ident) => (

fn $func_name() {

// Макрос `stringify!` преобразует `ident` в строку.

println!("Вызвана функция {:?}()",

stringify!($func_name))

}

)

}

// Создадим функции с именами `foo` и `bar` используя макрос, указанный выше.

create_function!(foo);

create_function!(bar);

macro_rules! print_result {

// Этот макрос принимает выражение типа `expr` и напечатает

// его как строку вместе с результатом.

// Указатель `expr` используют для обозначения выражений.

($expression:expr) => (

// `stringify!` преобразует выражение в строку *без изменений*.

println!("{:?} = {:?}",

stringify!($expression),

$expression);

)

}

fn main() {

foo();

bar();

print_result!(1u32 + 1);

// Напомним, что блоки тоже являются выражениями!

print_result!({

let x = 1u32;

x * x + 2 * x - 1

});

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Это список всех указателей:

   • block

   • expr используют для обозначения выражений

   • ident используют для обозначения имени переменной/функции

   • item

   • literal используется для литеральных констант

   • pat (образец)

   • path

   • stmt (единственный оператор)

   • tt (единственное дерево лексем)

   • ty (тип)

   • vis (спецификатор видимости)

Полный список указателей, вы можете увидеть в Rust Reference.

Перегрузка

Макросы могут быть перегружены, принимая различные комбинации аргументов. В этом плане, macro_rules! может работать аналогично блоку сопоставления (match):

// `test!` будет сравнивать `$left` и `$right`

// по разному, в зависимости от того, как вы объявите их:

macro_rules! test {

// Не нужно разделять аргументы запятой.

// Можно использовать любой шаблон!

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

println!("{:?} и {:?} это {:?}",

stringify!($left),

stringify!($right),

$left && $right)

);

// ^ каждый блок должен заканчиваться точкой с запятой.

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

println!("{:?} или {:?} это {:?}",

stringify!($left),

stringify!($right),

$left || $right)

);

}

fn main() {

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

test!(true; or false);

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Повторение

Макросы могут использовать знак + в списке аргументов, чтобы указать, какие аргументы могут повторяться хоть один раз, или знак *, чтобы указать, какие аргументы могут повторяться ноль или несколько раз.

В следующем примере, шаблон, окружённый $(...),+ будет сопоставлять одно или несколько выражений, разделённых запятыми. Также обратите внимание, что точка с запятой является необязательной в последнем случае.

// `min!` посчитает минимальное число аргументов.

macro_rules! find_min {

// Простой вариант:

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

// `$x` следует хотя бы одному `$y,`

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

// Вызовем `find_min!` на конце `$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

DRY (Не повторяйся)

Макросы позволяют писать DRY код, путём разделения общих частей функций и/или набор тестов. Вот пример, который реализует и тестирует операторы +=, *= и -= на Vec:

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

macro_rules! assert_equal_len {

// Указатель `tt` (единственное дерево лексем) используют для

// операторов и лексем.

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

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

"{:?}: несоответствие размеров: {:?} {:?} {:?}",

stringify!($func),

($a.len(),),

stringify!($op),

($b.len(),));

)

}

macro_rules! op {

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

fn $func + Copy>(xs: &mut Vec, ys: &Vec) {

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);

}