Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to write chained comparison macro in Rust?

Tags:

macros

rust

In rust, it's possible to pass > or <= etc inside macro arguments so long as the arguments are idents.

Is it possible to create a macro that lets you chain comparison operators?

let x = 3;
let y = 1;
let z = -3;

assert_eq!(cond!(z <= x > y), true);
like image 496
Hossain Adnan Avatar asked Jan 26 '23 09:01

Hossain Adnan


2 Answers

Yes you can. You need to use tt for the operator type:

macro_rules! cond {
    (@rec ($head:expr) $last:ident $op:tt $next:ident $($tail:tt)*) => {
        cond!(@rec (($head) && ($last $op $next)) $next $($tail)*)
    };
    (@rec ($head:expr) $last:ident) => { $head };
    ($first:ident $op:tt $next:ident $($tail:tt)*) => {
        cond!(@rec ($first $op $next) $next $($tail)*)
    }
}

fn main() {
    let x = 3;
    let y = 1;
    let z = -3;
    println!("(z <= x > y) = {}", cond!(z <= x > y));
}

Playground

You can also read The little book of Rust Macros for more advanced macros patterns.

like image 90
Jmb Avatar answered Jan 27 '23 21:01

Jmb


I think the following does what you expect as long as you are careful with the arguments to cond.

It uses tt (for argument operator0) to match <, <=, >=, etc. to avoid repeating lots of cases, but tt, of course, matches other tokens, too.

macro_rules! cond{
    ($x:ident $operator0:tt $x0:ident) => {
        ($x $operator0 $x0)
    };
    ($x:ident $operator0:tt $x0:ident $($operator1:tt $x1:ident)*) => {
        ($x $operator0 $x0) && cond!($x0 $($operator1 $x1)*)
    };
}

fn main() {
    let x = 3;
    let y = 1;
    let z = -3;

    assert_eq!(cond!(z <= x > y), true);
}
like image 31
phimuemue Avatar answered Jan 27 '23 22:01

phimuemue