虽然理论上是可能的,但您还是成功地 Select 了宏的一种较硬的边缘情况.主要原因是,如果不修改宏界外的区域,此宏无法展开到所需的等效代码.让我们用您的示例的略微修改的示例版本来实现这一点,以显示这是如何存在问题的.
if lets![Some(x) = a && Some(y) = b] {
// etc
}
这可能看起来相当明显,但不可能展开为两个嵌套的IF语句,因为这将需要在宏的范围之外使用额外的花括号.
if let Some(x) = a {
if let Some(y) = b {
// etc
} // <- How do we create this brace?
}
不过,简单的解决方案是将两个匹配合并为一个匹配.然而,通过提前计算这两个表达式,我们将失go 短路属性.不幸的是,这也是不可能的,因为if let
被认为是一个特殊的表达式,而发出let x = y
本身不是一个有效的表达式.这种情况在future 可能会改变,但我对此表示怀疑,因为这将需要在任何任意表达式中都允许变量声明.
// Theoretical macro expansion
if let (Some(x), Some(y)) = (a, b) {
// etc
}
现在我在这里思考了一段时间,但我想不出一种方法来弥补if let
是特殊运算符的事实,我们需要发出一些介于if
和{ /* etc */ }
之间的令牌,这些令牌可以用作if let
.即使是拥有完全访问权限以发出它们想要的任何令牌的过程性宏,也可能无法以您所描述的方式做到这一点.方便的是,在[铁 rust ]标签下提出的最新问题(Rust Procedural Macro - macro expansion ignores token `,`)提供了一些关于这一点的见解.过程性宏不能只是发出任何任意标记并期望被放入汇编代码中.它们必须在使用它们的上下文中有效.例如,我们不希望宏像链接问题中那样添加不匹配的圆括号或多个逗号分隔值.宏需要覆盖整个IF语句才能正确展开.
事实上,这可能就是广受欢迎的if_chain
crate 之所以能发挥作用的原因.如果你想要这个功能,又不想增加夜间功能,或者花太多时间弄清楚如何让它工作,那么这个 crate 可能是个不错的 Select .下面是他们的文档中有关如何使用宏的一个快速示例.
if_chain! {
if let Some(y) = x;
if y.len() == 2;
if let Some(z) = y;
then {
do_stuff_with(z);
}
}