6次发布
0.2.3 | 2024年3月15日 |
---|---|
0.2.2 | 2023年10月28日 |
0.2.0 | 2023年9月9日 |
0.1.1 | 2023年5月16日 |
在 过程宏 中排名第 2117
每月 54 次下载
用于 ssloc
53KB
823 行
forr
forr!
旨在用重复代码的目的来替换单次使用的 macro_rules!
。
例如,它可以减少多个元组的实现
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(&()));
使用 macro-rules,这将是这样
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(&()));
在这个例子中,它并不复杂,添加更多的元组变体实际上需要更少的代码。但要正确实现它,我尝试了很多次。(如果你不计我第一次实现整个库。)
用法
调用命令的第一部分是模式,类似于Rust中的正常 for
循环。这里你可以使用一个 单个变量,例如 $name:type
或一个 元组绑定 ($name:type, $nbme:type, ...)
。可以可选地在之前或之后指定 非消耗模式,目前这仅包括 :idx
。
之后是关键字 in
,一个包含要迭代的标记和 主体 的数组字面量 [...]
,主体用 $*
或 $:
标记。
单个变量绑定
forr! { $val:expr in [1, 2 + 4, 20]
$val
将是 1
,2 + 4
和 20
。
元组绑定
(
$name:type
, ... )
forr! { ($val:expr, $vbl:ty) in [(1, i32), (Ok(2 + 4), Result<u8, ()>), (20.0, f32)]
$val
将是 1
,Ok(2 + 4)
和 20.0
。
$vbl
将是 i32
,Result<u8, ()>
和 f32
。
非消耗模式
非消耗模式可以在消耗模式之前或之后指定,或者在元组绑定中使用。
目前,仅支持 :idx
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
将是 1
和 2
$i
将是 0
和 1
$vbl
将是 i32
主体
主体可以有两种不同的模式。当它以 $*
初始化时,整个主体将像正常的 for 循环一样重复。如果以 $:
开头,主体将更像使用 $()*
进行重复的宏展开。在两种情况下,当放置在 $()?
内时,将特别处理 [可选值](#optional values),最内层的此类组只有在存在值时才会添加。
$*
外部重复
在 $*
之后的所有标记中,每个 $ident
的出现,如果标识符与声明的变量之一匹配,则替换为相应的值。
forr! {$val:expr in [(1, "a", true)] $*
assert_eq!($val, $val);
}
将展开为
assert_eq!(1, 1);
assert_eq!("a", "a");
assert_eq!(true, true);
$:
内部重复
$:
允许在展开周围有非重复的代码,这在宏不允许的情况下非常有用。
forr! {($pat:expr, $res:expr) in [(0, true), (1, false), (2.., true)] $:
match 1u8 {
$($pat => $res,)*
}
}
如果没有内部重复,这将不可能实现,因为不允许将宏用作 match
的主体。
match 1 {
forr! {($pat:expr, $res:expr) in [(0, true), (1, false), (2.., true)] $*
$pat => $res
}
}
名称
任何有效的Rust标识符,包括关键字,都可以用来命名变量。注意,阴影不起作用,即内部的 forr!
需要使用不同的名称。
类型
:expr
因为这个使用了 TokenParser::next_expression()
,因此它将允许任何符合表达式 ,
规则的内容,即它不能包含 ;
,并且不能在类型/泛型(例如 HashMap::<A, B>
)的turbofish外使用逗号。
:ty
因为这个使用了 TokenParser::next_type()
,因此它将允许任何符合类型 ,
规则的内容,即它不能包含 ;
,并且在泛型(例如 HashMap<A, B>
)中不能在 <>
外使用逗号,也不能使用未打开的闭合 >
。
:tt
目前正好匹配一个 proc_macro::TokenTree
,但计划扩展到与 macro_rule!
的 :tt
匹配。
:inner
最通用的类型,允许任意括号([
,{
或 (
... )}]
)包裹的标记。
:idx
非消耗模式,将包含当前索引。
iff!
旨在是 #[cfg(...)]
的一个替代版本,能够有条件地启用任何Rust标记,并且能够,例如比较标记,但不能有条件地使用其他实际 cfg
或特性。
iff! { true && false $:
compile_error!("This is not expanded")
}
iff! { true || false $:
compile_error!("This is expanded")
}
除了基本的布尔运算(!
,&&
,||
)之外,还有一些函数
empty(<tokens>)
检查<tokens>
是否为空。equals(<lhs>)(<rhs>)
检查<lhs>
是否与<rhs>
相等。
iff! { empty() $:
compile_error!("This is expanded")
}
iff! { empty(something) $:
compile_error!("This is not expanded")
}
iff! { equals(something)(another thing) $:
compile_error!("Neither is this")
}
依赖项
~235KB