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

Download history 129/week @ 2024-04-08 32/week @ 2024-04-15 51/week @ 2024-04-22 34/week @ 2024-04-29 33/week @ 2024-05-06 14/week @ 2024-05-13 14/week @ 2024-05-20 7/week @ 2024-06-03 13/week @ 2024-06-10 1/week @ 2024-06-17 6/week @ 2024-07-01 7/week @ 2024-07-15 41/week @ 2024-07-22

每月 54 次下载
用于 ssloc

MIT/Apache

53KB
823

forr

CI Status Crates.io Docs.rs Documentation for main

控制流循环 在编译时。

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,一个包含要迭代的标记和 主体 的数组字面量 [...],主体用 $*$: 标记。

单个变量绑定

$ name : type

forr! { $val:expr in [1, 2 + 4, 20]

$val 将是 12 + 420

元组绑定

( $name:type, ... )

forr! { ($val:expr, $vbl:ty) in [(1, i32), (Ok(2 + 4), Result<u8, ()>), (20.0, f32)]

$val 将是 1Ok(2 + 4)20.0

$vbl 将是 i32Result<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 将是 12

$i 将是 01

$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