5 个不稳定版本
使用旧 Rust 2015
0.3.1 | 2017年1月20日 |
---|---|
0.3.0 | 2016年8月13日 |
0.2.0 | 2016年5月21日 |
0.1.1 | 2016年2月23日 |
0.1.0 | 2016年2月23日 |
在 Rust 模式 中排名第 988
23KB
262 行
这是什么
这是一个小的 Rust 包,通过一个宏提供了一个新的控制流原语。
RFC 243(?
/catch
RFC)提议了一个名为“从任何块中提前退出”的功能。它将break
泛化,使其可以接受一个表达式和一个生命周期,并在所有{}
块中使用,而不仅仅是循环。 break LIFE EXPR
从由生命周期指定的块/循环中退出,并从循环中返回给定的表达式。当然,该表达式必须与块正常结束时返回的值具有相同的类型。
我们可以通过源到源的转换来指定所需的功能(我怀疑如果该功能被添加到语言中,这将不是如何完成的,但这确实表明不需要新的语言功能或控制流原语)
输入
let x = 'a: {
break 'a 0; // *
1 // **
};
输出(星号表示对应行)
let x = {
let ret;
'a: loop {
ret = {
ret = 0; // *
break 'a; // *
1 // **
};
break 'a;
}
ret
};
嗯,写这个很麻烦(而且读起来也很麻烦)。我们不要再这样做了。
#[macro_use] extern crate named_block;
#[macro_use] extern crate static_cond; // not needed on nightly
let x = block!('a: {
break 'a 0;
1
});
修复了!
如何使用它
首先,在 Cargo.toml
中将 "named-block" 添加为依赖项。然后,在您的包根目录顶部添加 #[macro_use] extern crate named_block;
。
如果您使用的是 nightly Rust,则可以启用 "nightly" Cargo 功能并跳过第二步。否则,您需要在 Cargo.toml
中添加 "static-cond",并添加 #[macro_use] extern crate static_cond;
。 (检查此包的 Cargo.toml
以查看应使用哪个版本的 "static-cond"。)
它是如何工作的
宏 block!
通过递归(很多)遍历您的代码,并执行上述描述的源到源转换。变量 ret
使用 hygiene 生成,因此不会与其他变量名或嵌套调用 block!
发生冲突。一些巧妙的宏技巧包括使用“解析栈”进入标记树,并在运行时生成新的宏进行比较。请参阅注释的宏源代码以获取更多详细信息。
限制
-
该宏会递归。非常多。这意味着它将使编译速度相对于块中代码的长度而减慢。您可能需要增加递归限制(在crate根目录处插入
#![recursion_limit = ""1000""]
,根据需要调整数字)。 -
break LIFE EXPR
几乎在任何地方出现时都会被转换。-
即使它位于另一个宏的调用中,如
block!('a: { foo!(break 'a 42) })
。原则上,foo!
可能打算以某种方式转换语法,而block!
会将其搞砸。但看起来更有可能的是,您确实希望宏调用中的代码被转换。 -
即使它位于闭包内部。这在罕见情况下可能会导致问题。如果(a)您在
block!
调用内部有一个闭包,并且(b)闭包内部有一个block!
调用,以及(c)块标签相同...那么您将收到一些古怪的错误消息和/或行为。 -
该宏足够智能,可以忽略项目。因此,局部
fn
、impl
等内的块是安全的。这也应该会加快解析速度--一旦宏看到例如impl
关键字,它就可以跳过一个整个项目,而无需复制每个标记或下降到标记树。 -
对于闭包、奇怪的宏或其他未发现的宏错误,有一个特殊的安全出口,形式为属性。任何带有
#[block(ignore)]
注释的标记树将由宏忽略(这不要求#![feature(stmt_expr_attributes)]
,因为属性是由宏本身解析的)。示例
block!('a: { enum Foo { Bar(i32) } let closure = #[block(ignore)] { move |Foo::Bar(x): Foo| -> i32 { x + block!('a: { break 'a 41; }) } }; closure(Foo::Bar(1)) });
此块的结果是
42
。
-
-
缺少特定生命周期的裸
break/
continue
语句不允许在block!
调用中。这是因为宏展开本身会产生一个隐藏的循环,所以这些语句的结果将会是令人困惑且非预期的(类型错误、无限循环等)。同样地,你也不能在continue 'a
(其中'a
是分配给block!
的标签)的位置执行。宏在展开过程中会捕获所有这些情况,并产生编译错误。