例如,设想一个理论上为lets!的宏,其编写方式如下:

if lets![Some(x) = x && Some(y) = y] {
  ...
}

它将扩展为类似以下内容:

if let Some(x) = x {
  if let Some(y) = y {
    ...
  }
}

我知道很快就会有if-let-chains家连锁店来到Rust,所以这里的问题并不是关于let家连锁店.相反,它更多地是为了提供最终不安全的宏,因此生成的代码应该仅在宏展开后才进行解析--例如,设想一个宏填入if条件的一部分,并影响扩展后的代码应该如何解析.

对于铁 rust 来说,这样的事情可能发生吗?

推荐答案

虽然理论上是可能的,但您还是成功地 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);
    }
}

Rust相关问答推荐

即使参数和结果具有相同类型,fn的TypId也会不同

为什么是!为Rust中的RwLockReadGuard和RwLockWriteGuard实现的发送特征?

在rust中如何修改一个盒装函数并将其赋回?

在Rust中,有没有一种方法让我定义两个 struct ,其中两个都遵循标准 struct ?

在Rust中宏的表达式中提取对象

如何在 struct 的自定义序列化程序中使用serde序列化_WITH

rust中的库插件管理器,现在是否可行?

write_buffer 不写入缓冲区而是输出零 WGPU

如何处理闭包中的生命周期以及作为参数和返回类型的闭包?

返回迭代器考虑静态生命周期类型

闭包返回类型的生命周期规范

中文优化标题:跳出特定循环并返回一个值

在发布中包含 Rust DLL

Rust 中的自动取消引用是如何工作的?

在构建器模式中捕获 &str 时如何使用生命周期?

如何在 Rust 中将 UTF-8 十六进制值转换为 char?

是否可以在 Rust 中的特定字符上实现特征?

为什么我可以同时传递可变和不可变引用?

守卫如何影响匹配语句?

您不能borrow 对只读值的可变引用