Есть ли способ сопоставить тело оператора сравнения с макросом Rust? - PullRequest
0 голосов
/ 22 сентября 2018

Я имею в виду что-то вроде этого:

macro_rules! mymatch {
   ($obj:ident, ($matcher:tt => $result:tt),*) => {
       match $obj {
           $matcher
       },
   }
}

mymatch! {
    x,
    10 => "Ten",
    n if n < 5 => "Less than 5"
}

Я не хочу переопределять оператор match, но я хочу поместить дополнительные вещи с обеих сторон.Тем не менее, я не могу понять, как сопоставить содержимое оператора сравнения.

1 Ответ

0 голосов
/ 22 сентября 2018

Там несколько вещей не так с вашим синтаксисом.Если вы хотите сопоставить повторяющийся шаблон, вам нужно начать группировку с $, например: $($matcher:tt => $result:tt),*.

Далее вы, вероятно, захотите сопоставить больше, чем идентификаторы, поскольку любое выражение допустимо для сопоставления, поэтому измените его на expr.Слева от => вам понадобится pat (шаблон), а не tt.Дерево токенов является общим, но, например, в n if n < 5 будет соответствовать 5 отдельным токенам - n, if, n, <, 5 - и вы, как правило, отступаете от этого только тогда, когда ничегоеще работает.То же самое для результата - это должно быть expr (выражение).

Игнорируя охрану if < 5, вы можете сопоставить следующее:

my_match! {
    x,
    10 => "Ten",
    _ => "something else"
}

со следующиммакрос:

macro_rules! my_match {
   ($obj:expr, $($matcher:pat => $result:expr),*) => {
       match $obj {
           $($matcher => $result),*
       }
   }
}

Охранники раздражают, потому что они необязательны, но квантификатор ? еще не стабилен.Вместо этого вы должны использовать * (0 или больше), даже если это будет технически соответствовать больше, чем вам нужно.

Полный макрос имеет следующий вид:

macro_rules! my_match {
   ($obj:expr, $($matcher:pat $(if $pred:expr)* => $result:expr),*) => {
       match $obj {
           $($matcher $(if $pred)* => $result),*
       }
   }
}

и поддерживает это использование:

let x = 7;
let s = my_match! {
    x,
    10 => "Ten",
    n if x < 5 => "Less than 5",
    _ => "something else"
};
println!("s = {:?}", s); // "Something else" 

Блок также является expr, так что это также допустимо:

my_match! { 
    x,
    10 => "Ten",
    n if x < 5 => { 
        println!("it was {}", n); 
        "Less than 5" 
    },
    _ => "something else"
};
...