3
\$\begingroup\$

I'm very new to Rust and as I've been going through the book I became very interested in its macros. To better understand them I tried to write something of a usable list comprehension like those in Haskell and Python. I've asked and had answered a question on SO in the process: Rust macro error: local ambiguity: multiple parsing options

Ultimately, I've ended up with what's below but I still find that I'm not at ease that my choices of fragment types is necessarily optimal (or even correct besides working in my examples).

If it's not clear from my test examples at the bottom of the code, the goal is to allow destructive iterators and let statements in generating the "list" and call the macro recursively to scope things in deeper and deeper loops.

What improvements could be made to make this more robust and correct?

macro_rules! comp {

    ($f: expr, $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)*) => {{
       comp![$f, $x<-$iterx; $(let $s= $v;)*, true]
    }};
    ($f: expr, $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)* $(| $y: ident <- $itery:expr; $(let $t: ident = $w:expr;)*)*) => {
        comp![$f, $x <- $iterx; $(let $s = $v;)* $(| $y <- $itery; $(let $t = $w;)*)*, true]
    };
    ($f: expr, $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)* $(| $y: ident <- $itery:expr; $(let $t: ident = $w:expr;)*)+, $cond: expr) => {{
        let mut myvec = Vec::new();
        let iter=$iterx;
        for $x in iter {
            $(let $s = $v;)*
            comp![$f, $(| $y <- $itery; $(let $t = $w;)*)+, $cond; myvec]
        }
        myvec
    }};

    ($f: expr, $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)*, $cond: expr) => {{
        let mut myvec = Vec::new();
        let iter=$iterx;
        for $x in iter {
            $(let $s = $v;)*
            if $cond {
                myvec.push($f);
            };
        }
        myvec
    }};
    ($f: expr, | $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)*, $cond: expr; $myvec: ident) => {{
        let iter=$iterx;
        for $x in iter {
            $(let $s = $v;)*
            if $cond {
                $myvec.push($f);
            };
        }
    }};
    ($f: expr, | $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)* $(| $y: ident <- $itery:expr; $(let $t: ident = $w:expr;)*)+; $cond: expr; $myvec: ident) => {{
        let iter=$iterx;
        for $x in iter {
            $(let $s = $v;)*
            comp![$f, $(| $y <- $itery; $(let $t = $w;)*)+, $cond; $myvec]
        }
    }};
}

#[cfg(test)]
mod tests {
    #[test]
    fn test() {
        assert_eq!(
            comp![(i,j), i <- 1..3; | j <- 1..5; ],
            vec![
                (1, 1),
                (1, 2),
                (1, 3),
                (1, 4),
                (2, 1),
                (2, 2),
                (2, 3),
                (2, 4)
            ]
        );
        assert_eq!(
            comp![ (i,j), i <- 1..4; let k = i*i;| j <- 1..k; ],
            vec![
                (2, 1),
                (2, 2),
                (2, 3),
                (3, 1),
                (3, 2),
                (3, 3),
                (3, 4),
                (3, 5),
                (3, 6),
                (3, 7),
                (3, 8)
            ]
        );
        assert_eq!(
            comp![ comp![ (i,j) , i <- 1..3; ] , j <- 1..6;],
            vec![
                [(1, 1), (2, 1)],
                [(1, 2), (2, 2)],
                [(1, 3), (2, 3)],
                [(1, 4), (2, 4)],
                [(1, 5), (2, 5)]
            ]
        );
        assert_eq!(
            comp![(i,j), i <- 1..3; | j <- 1..5;, j>3],
            vec![(1, 4), (2, 4)]
        );
        assert_eq!(
            comp![ (i,j), i <- 1..4; let k = i*i;| j <- 1..k;, j>3],
            vec![(3, 4), (3, 5), (3, 6), (3, 7), (3, 8)]
        );
        assert_eq!(
            comp![ comp![ (i,j) , i <- 1..3; ] , j <- 1..6;, j>3],
            vec![[(1, 4), (2, 4)], [(1, 5), (2, 5)]]
        );
        assert_eq!(
            comp![ comp![ (i,j, iv*ivv, k) , i <- vec![0,2,4,5].into_iter(); let iv=i*i*i; let ivv=iv*3;] , j <- vec![0,2,4,5].into_iter(); | k <-9..17; let kv=j*k;, kv*kv>400],
            vec![
                [
                    (0, 2, 0, 11),
                    (2, 2, 192, 11),
                    (4, 2, 12288, 11),
                    (5, 2, 46875, 11)
                ],
                [
                    (0, 2, 0, 12),
                    (2, 2, 192, 12),
                    (4, 2, 12288, 12),
                    (5, 2, 46875, 12)
                ],
                [
                    (0, 2, 0, 13),
                    (2, 2, 192, 13),
                    (4, 2, 12288, 13),
                    (5, 2, 46875, 13)
                ],
                [
                    (0, 2, 0, 14),
                    (2, 2, 192, 14),
                    (4, 2, 12288, 14),
                    (5, 2, 46875, 14)
                ],
                [
                    (0, 2, 0, 15),
                    (2, 2, 192, 15),
                    (4, 2, 12288, 15),
                    (5, 2, 46875, 15)
                ],
                [
                    (0, 2, 0, 16),
                    (2, 2, 192, 16),
                    (4, 2, 12288, 16),
                    (5, 2, 46875, 16)
                ],
                [
                    (0, 4, 0, 9),
                    (2, 4, 192, 9),
                    (4, 4, 12288, 9),
                    (5, 4, 46875, 9)
                ],
                [
                    (0, 4, 0, 10),
                    (2, 4, 192, 10),
                    (4, 4, 12288, 10),
                    (5, 4, 46875, 10)
                ],
                [
                    (0, 4, 0, 11),
                    (2, 4, 192, 11),
                    (4, 4, 12288, 11),
                    (5, 4, 46875, 11)
                ],
                [
                    (0, 4, 0, 12),
                    (2, 4, 192, 12),
                    (4, 4, 12288, 12),
                    (5, 4, 46875, 12)
                ],
                [
                    (0, 4, 0, 13),
                    (2, 4, 192, 13),
                    (4, 4, 12288, 13),
                    (5, 4, 46875, 13)
                ],
                [
                    (0, 4, 0, 14),
                    (2, 4, 192, 14),
                    (4, 4, 12288, 14),
                    (5, 4, 46875, 14)
                ],
                [
                    (0, 4, 0, 15),
                    (2, 4, 192, 15),
                    (4, 4, 12288, 15),
                    (5, 4, 46875, 15)
                ],
                [
                    (0, 4, 0, 16),
                    (2, 4, 192, 16),
                    (4, 4, 12288, 16),
                    (5, 4, 46875, 16)
                ],
                [
                    (0, 5, 0, 9),
                    (2, 5, 192, 9),
                    (4, 5, 12288, 9),
                    (5, 5, 46875, 9)
                ],
                [
                    (0, 5, 0, 10),
                    (2, 5, 192, 10),
                    (4, 5, 12288, 10),
                    (5, 5, 46875, 10)
                ],
                [
                    (0, 5, 0, 11),
                    (2, 5, 192, 11),
                    (4, 5, 12288, 11),
                    (5, 5, 46875, 11)
                ],
                [
                    (0, 5, 0, 12),
                    (2, 5, 192, 12),
                    (4, 5, 12288, 12),
                    (5, 5, 46875, 12)
                ],
                [
                    (0, 5, 0, 13),
                    (2, 5, 192, 13),
                    (4, 5, 12288, 13),
                    (5, 5, 46875, 13)
                ],
                [
                    (0, 5, 0, 14),
                    (2, 5, 192, 14),
                    (4, 5, 12288, 14),
                    (5, 5, 46875, 14)
                ],
                [
                    (0, 5, 0, 15),
                    (2, 5, 192, 15),
                    (4, 5, 12288, 15),
                    (5, 5, 46875, 15)
                ],
                [
                    (0, 5, 0, 16),
                    (2, 5, 192, 16),
                    (4, 5, 12288, 16),
                    (5, 5, 46875, 16)
                ]
            ]
        );
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.