如何在Rust中编写一个宏来匹配集合中的任何元素?

时间:2016-08-08 12:11:49

标签: macros rust variadic-macros

在C中,我习惯了:

if (ELEM(value, a, b, c)) { ... }

这是一个带有可变数量参数的宏,以避免输入

if (value == a || value == b || value == c) { ... }

可以在Varargs `ELEM` macro for use with C中看到C示例。

这可能在Rust吗?我假设它会使用match。如果是这样,如何使用可变参数来实现这一目标?

4 个答案:

答案 0 :(得分:4)

macro_rules! cmp {
    // Hack for Rust v1.11 and prior.
    (@as_expr $e:expr) => { $e };

    ($lhs:expr, $cmp:tt any $($rhss:expr),*) => {
        // We do this to bind `$lhs` to a name so we don't evaluate it multiple
        // times.  Use a leading underscore to avoid an unused variable warning
        // in the degenerate case of no `rhs`s.
        match $lhs { _lhs => {
            false || $(
                cmp!(@as_expr _lhs $cmp $rhss)
            ) || *
        //    ^- this is used as a *separator* between terms
        }}
    };

    // Same, but for "all".
    ($lhs:expr, $cmp:tt all $($rhss:expr),*) => {
        match $lhs { _lhs => {
            true && $( cmp!(@as_expr _lhs $cmp $rhss) ) && *
        }}
    };
}

fn main() {
    let value = 2;
    if cmp!(value, == any 1, 2, 3) {
        println!("true! value: {:?}", value);
    }
    if cmp!(value*2, != all 5, 7, 1<<7 - 1) {
        println!("true! value: {:?}", value);
    }
}

答案 1 :(得分:2)

首先,如果您的abc是具体值,则可以使用match

fn main() {
    let x = 42;

    match x {
        1 | 2 | 3 => println!("foo"),
        42 => println!("bar"),
        _ => println!("nope"),
    }
}

如果你想匹配变量,你需要像这样编写match武器:

match x {
    x if x == a || x == b || x == c => println!("foo"),
    42 => println!("bar"),
    _ => println!("nope"),
}

...这基本上是你想要避免的。

但是:也可以直接翻译你的C宏!

macro_rules! elem {
    ($val:expr, $($var:expr),*) => {
        $($val == $var)||*
    }
}

fn main() {
    let y = 42;
    let x = 42;

    if elem!(x, 1, 3, y) {
        println!("{}", x);
    }
}

答案 2 :(得分:1)

我偏爱在没有宏的情况下写这个,利用数组上的contains

fn main() {
    if [1, 2, 3, 4].contains(&4) {
        println!("OK");
    }
}

很难预测在优化时会发生什么,但如果绝对性能是一个目标,那么您可以很好地对每种方法进行基准测试。

答案 3 :(得分:0)

是的,这是可能的,下面的宏会扩展以进行每次检查。

macro_rules! elem {
    ($n:expr, $( $hs:expr ),*) => ($( $n == $hs )||* );
}

fn main() {
    if elem!(4, 1, 2, 3, 4) {
        println!("OK");
    }
}

感谢IRC中#vust上的@vfs。