4 个版本
0.1.3 | 2019 年 5 月 11 日 |
---|---|
0.1.2 | 2019 年 4 月 21 日 |
0.1.1 | 2019 年 4 月 20 日 |
0.1.0 | 2019 年 4 月 20 日 |
#1339 in Rust 模式
18KB
191 行
py-comp - 一个实现类似 Python 生成器表达式的 Rust 宏
此宏实现了一种语法,模拟 Python 的 生成器表达式
语法,并使其更符合 Rust 的常规语法。
这意味着 Python 语法与该宏提供的语法之间有一些细微的差异
for
和in
符号之间的模式是一个完整的 Rust 模式,可以是简单的符号,也可以是结构体解构等复杂形式。- 在
in
符号之后定义迭代器的表达式必须评估为Iterator
或impl IntoIterator
。 - 在
if
符号之后的条件表达式必须评估为布尔值。 - 您可以在允许使用
if
子句的地方使用if let
子句代替常规的if
子句。在if let
子句中引入的任何名称都可在任何后续子句中使用。 - 生成器表达式开始的表达式、
in
符号之后的表达式以及if
符号之后的表达式都必须以分号 (;) 结尾。唯一的例外是宏中in
或if
之后的最后一个表达式,可以省略尾随的分号。
由 comp!()
宏调用替换的表达式是一个惰性迭代器,其生命周期受其需要捕获的任何引用的限制。这意味着它可以被 .collect()
到您喜欢的任何容器中。
请注意,至少目前,在 in
子句中命名的所有对象(除了第一个 in
子句)必须是 Copy
类型,或者是由前面的 for
或 if let
子句引入的。这是因为宏使用了一个 move
闭包(FnOnce
)来处理每一层嵌套,这可能会导致捕获的对象需要被实例化多次,而不进行隐式克隆。同样,在 "yield" 表达式中命名的对象(位于第一个 for
子句之前)如果它们不是由最后的 for
或 if let
子句引入的,也必须是 Copy
类型。这是因为它们可能被用于多个输出项。
将来可能会添加指定应该克隆哪些对象及其位置的功能,但这可能需要引入破坏性更改。
这是此宏使用的语法的 BNF 描述。
comprehension ::= expression ";" comp_for [comp_iter] [";"]
comp_iter ::= ";" (comp_for | comp_if | comp_if_let)
comp_for ::= "for" pattern "in" expression [comp_iter]
comp_if ::= "if" expression [comp_iter]
comp_if_let ::= "if" "let" pattern ("|" pattern)* "=" expression [comp_iter]
就像在 Python 中一样,你可以嵌套任意多的 for
、if
和 if let
子句。
示例
带有条件的简单生成器表达式
use py_comp::comp;
#[derive(Debug, PartialEq, Eq)]
struct Foo(i32);
let arr = &[Foo(11), Foo(12)];
// Notice the semicolons
let comp_vector = comp!(item; for item in arr; if item.0 % 10 == 2)
.collect::<Vec<&Foo>>();
assert_eq!(comp_vector, vec![&Foo(12)])
带有条件和模式的笛卡尔积的三重产品
use py_comp::comp;
#[derive(Debug, PartialEq, Eq)]
struct Foo(i32);
// These need to be references to arrays because of how the closures
// that the macro expands to capture their environment.
let x = &[(Foo(11), "foo"), (Foo(12), "bar")];
let y = &[Foo(21), Foo(22)];
let z = &[Foo(31), Foo(32)];
let xyz = comp!(
(a, b, c);
for (a, _text) in x; // You can use any function parameter pattern.
if a.0 % 10 == 2;
for b in y; // Obviously not every level requires a conditional.
for c in z;
if c.0 % 10 == 2;
)
.collect::<Vec<(&Foo, &Foo, &Foo)>>();
// The result vector here is short for illustration purposes
// but can be as long as long as you need it to be.
assert_eq!(xyz, vec![(&Foo(12), &Foo(21), &Foo(32)), (&Foo(12), &Foo(22), &Foo(32))])
展开三重嵌套结构 + 复杂表达式
use py_comp::comp;
#[derive(Debug, PartialEq, Eq)]
struct Foo(i32);
let nested_3 = &[
[
[Foo(0), Foo(1), Foo(2)],
[Foo(3), Foo(4), Foo(5)],
[Foo(6), Foo(7), Foo(8)],
],
[
[Foo(9), Foo(10), Foo(11)],
[Foo(12), Foo(13), Foo(14)],
[Foo(15), Foo(16), Foo(17)],
],
[
[Foo(18), Foo(19), Foo(20)],
[Foo(21), Foo(22), Foo(23)],
[Foo(24), Foo(25), Foo(26)],
],
];
let nested_objects = comp!(
{
let inner = nested.0;
Foo(inner + 1)
};
for nested_2 in nested_3;
for nested_1 in nested_2;
for nested in nested_1;
)
.collect::<Vec<Foo>>();
let expected_values = (1..28).map(Foo).collect::<Vec<Foo>>();
assert_eq!(expected_values, nested_objects);
依赖项
~9KB