A for loop in compile time
Aims to replace single use macro_rules! for the purpose to repeating code.
For example it can reduce an implementation for multiple tuples: ```rust use forr::forr; trait Like { fn like (&self, other: &Self) -> bool; } impl Like for i32 { fn like (&self, other: &Self) -> bool { self == other } }
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:
```rust macrorules! impllikefortuples { [$(($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 } } $(impllikefortuples![$(($gens, $idxs)),*,];)? } } impllikefortuples![(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 trys 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 or a tuple
binding ($name:type, $nbme:type, ...). There can
optionally be non consuming patterns specified
before or after, 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 $:.
$ name : type
rust
forr! { $val:expr in [1, 2 + 4, 20]
$val will be 1, 2 + 4 and 20.
( $name:type, ... )
rust
forr! { ($val:expr, $vbl:ty) in [(1, i32), (Ok(2 + 4), Result<u8, ()>), (20.0, f32)]
$val will be 1, Ok(2 + 4) and 20.0.
$vbl will be i32, Result<u8, ()> and f32.
Non consuming patterns can be specified before or after the consuming patterns or inside if using a tuple binding.
Currently, only :idx is supported
rust
forr! { $val:expr, $i:idx in [1, 2]
forr! { $i:idx, $val:expr in [1, 2]
forr! { $i:idx, ($val:expr, $vbl:ty) in [(1, i32), (2, i32)]
forr! { ($val:expr, $vbl:ty), $i:idx in [(1, i32), (2, i32)]
forr! { ($val:expr, $i:idx, $vbl:ty) in [(1, i32), (2, i32)]
$val will be 1 and 2
$i will be 0 and 1
$vbl will be i32
The body can be in two different modes. When it is initialized with $* the
whole body is repeated similar to a normal for loop. Is it started with
$:, the body will behave more like macro expansion using $()* for
repetition. In both cases there is special handling for optional
values when placed inside $()? the innermost such group
is only added if the value is present.
$* outer repetitionIn the tokens following the $* every occurrence of a $ident where the
ident matches one of the declared variables is replaced with the
corresponding value.
rust
forr! {$val:expr in [(1, "a", true)] $*
assert_eq!($val, $val);
}
will expand to
rust
assert_eq!(1, 1);
assert_eq!("a", "a");
assert_eq!(true, true);
$: inner repetition$: allows to have non repeated code surrounding the expansion, mainly
useful for cases where a macro would not be allowed.
rust
forr! {($pat:expr, $res:expr) in [(0, true), (1, false), (2.., true)] $:
match 1u8 {
$($pat => $res,)*
}
}
Without the inner repetition this would not be possible, as macros are not
allowed as the body of a match.
rust
match 1 {
forr! {($pat:expr, $res:expr) in [(0, true), (1, false), (2.., true)] $*
$pat => $res
}
}
Any valid rust idents including keywords can be used to name variables. Note
that shadowing does not work, i.e. an inner forr! needs to use different
names.
:exprAs this uses TokenParser::next_expression() this will allow anything
that matches the , rules for expressions i.e. it cannot contain ; and no
, outside turbofishes for Types/Generics (HashMap::<A, B>).
:tyAs this uses TokenParser::next_type() this will allow anything
that matches the , rules for types i.e. it cannot contain ; and no
, outside <> for Generics (HashMap<A, B>) and unopened closing >.
:ttCurrently matches exactly one proc_macro::TokenTree, but the plan is to extend this to what macro_rule!'s :tt matches.
:innerThe most versatile type, allowing arbitrary tokens wrapped by any bracket
[, { or ( ... )}].
:idxNon consuming pattern that will contain the current index.