Crate forr

source ·
Expand description

Control flow and loops in compile time.

§forr!

Aims to replace single use macro_rules! for the purpose to repeating code.

For example, it can reduce an implementation for multiple tuples:

forr! { $gens:inner in [(), (A,), (A, B), (A, B, C),
                        (A, B, C, D), (A, B, C, D, E)] $*
    // $idx is special this would be like (idx, gen) = [...].enumerate()
    forr! { $idx:idx, $gen:expr in [$gens] $:
        impl<$($gen: Like,)*> Like for ($gens) {
            fn like(&self, other: &Self) -> bool {
                $(self.$idx.like(&other.$idx)&&)* true
            }
        }
    }
}

assert!((1, 3).like(&(1, 3)));
assert!((1,).like(&(1,)));
assert!(().like(&()));

With macro-rules this would be:

macro_rules! impl_like_for_tuples {
    [$(($gen:ident, $idx:tt), $(($gens:ident, $idxs:tt)),*)?$(,)?] => {
        impl$(<$gen: Like, $($gens: Like),*>)? Like for ($($gen, $($gens),*)?) {
            fn like(&self, other: &Self) -> bool {
                $(self.$idx.like(&other.$idx) &&)?
                $($(self.$idxs.like(&other.$idxs) &&)*)?
                true
            }
        }
        $(impl_like_for_tuples![$(($gens, $idxs)),*,];)?
    }
}
impl_like_for_tuples![(E, 4), (D, 3), (C, 2), (B, 1), (A, 0)];

assert!((1, 3).like(&(1, 3)));
assert!((1,).like(&(1,)));
assert!(().like(&()));

Granted in this example it is not a lot more complicated, and adding more tuple variants actually requires less code. But it took me quite a few more tries getting it to work correctly. (If you don’t count me implementing this whole library for the first one.)

The first part of the invocation is the pattern, similar to a normal for loop in rust. Here you can use either a single variable i.e. $name:type, a tuple binding ($name:type, $nbme:type, ...), or a function casing($a:s). There can optionally be non consuming patterns specified, currently that includes only :idx.

This is followed by the keyword in, an array literal [...] containing the tokens to iterate and the body marked with either $* or $:.

For better support within macro_rules, # can be used instead of $, but only if the body was started with #* or #: instead of $*/$#.

For more details see forr!.

§iff!

Aims to be an alternative version of #[cfg(...)] able to conditionally enable any rust tokens and being able to, e.g. compare tokens, but it is not able to be conditional other actual cfg or features.

iff! { true && false $:
    compile_error!("This is not expanded")
}
iff! { true || false $:
    compile_error!("This is expanded")
}

On top of the basic boolean operations (!, &&, ||) there are some functions:

  • empty(<tokens>) tests if <tokens> is empty.
  • equals(<lhs>)(<rhs>) tests if <lhs> is equal to <rhs>.
  • equals_any(<lhs>)[(<rhs>), (<rhs>), ...] tests if <lhs> is equal to any <rhs>.

For better support within macro_rules, #: can be used instead of $:.

For more details see iff!.

Macros§

  • Iterates over specified list and expands input similar to macro_rules.
  • if for macro expansions.