问题
In C, I'm used to having:
if (ELEM(value, a, b, c)) { ... }
which is a macro with a variable number of arguments to avoid typing out
if (value == a || value == b || value == c) { ... }
A C example can be seen in Varargs `ELEM` macro for use with C.
Is this possible in Rust? I assume it would use match
. If so, how would variadic arguments be used to achieve this?
回答1:
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);
}
}
回答2:
First off, if your a
, b
, and c
are concrete values, you can just use match
:
fn main() {
let x = 42;
match x {
1 | 2 | 3 => println!("foo"),
42 => println!("bar"),
_ => println!("nope"),
}
}
If you want to match on variables you need to write the match
arms like this:
match x {
x if x == a || x == b || x == c => println!("foo"),
42 => println!("bar"),
_ => println!("nope"),
}
…which is basically what you want to avoid.
But: A pretty direct translation of your C macro is also possible!
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);
}
}
回答3:
I'm partial to writing this without a macro, taking advantage of contains
on arrays.
fn main() {
if [1, 2, 3, 4].contains(&4) {
println!("OK");
}
}
It's hard to predict what will happen to this when optimized, but if absolute performance is a goal you'd do well to benchmark each approach.
回答4:
Yes this is possible, the following macro expands to do each check.
macro_rules! elem {
($n:expr, $( $hs:expr ),*) => ($( $n == $hs )||* );
}
fn main() {
if elem!(4, 1, 2, 3, 4) {
println!("OK");
}
}
Thanks to @vfs on #rust in IRC.
来源:https://stackoverflow.com/questions/38828920/how-to-write-a-macro-in-rust-to-match-any-element-in-a-set