#let #if #else #1303

guard

按照RFC 1303实现的宏:类似于Swift的guard-let-else语句

16次发布

使用旧的Rust 2015

0.5.2 2023年6月4日
0.5.1 2021年4月11日
0.5.0 2018年12月26日
0.3.4 2018年3月22日
0.1.1 2015年11月27日

Rust模式中排名443

Download history 1477/week @ 2024-03-13 260/week @ 2024-03-20 222/week @ 2024-03-27 355/week @ 2024-04-03 292/week @ 2024-04-10 173/week @ 2024-04-17 126/week @ 2024-04-24 115/week @ 2024-05-01 126/week @ 2024-05-08 103/week @ 2024-05-15 127/week @ 2024-05-22 97/week @ 2024-05-29 106/week @ 2024-06-05 156/week @ 2024-06-12 109/week @ 2024-06-19 126/week @ 2024-06-26

每月下载量515
用于 4 crate

MIT/Apache

30KB
348

guard

Travis CI

这个特性已经正式加入到Rust中。当#![feature(let_else)]稳定后,这个crate应该被视为过时。

此crate导出了一个宏,实现了RFC 3137(最初为RFC 1303)的大部分功能,这是一种类似于Swift中的“let-else”或“guard”表达式。

RFC中确定的语法是let PAT = EXPR else { BODY }(其中BODY必须发散)。此宏理解并实现了编译器中已实现的多数功能,同时也有一些在第一个RFC中提出的变体,即在中间的else子句。

该crate还实现了一个名为guard_unwrap的变体,如果匹配失败则引发恐慌。

示例

#[macro_use] extern crate guard;
use std::env;

fn main() {
    // read configuration from a certain environment variable
    // do nothing if the variable is missing
    guard!(let Ok(foo) = env::var("FOO") else { return });

    println!("FOO = {}", foo);
}

Cargo功能

  • nightly在历史上是必需的,用于在夜间编译器编译时避免警告。现在它不再执行任何操作,仅保留以支持向后兼容。
  • debug启用trace_macros以进行调试。需要夜间编译器(但不需此crate的nightly功能)。

工作原理

将这种行为实现为宏很困难,因为必须在封装作用域中创建一个 let 语句。除此之外,还希望避免重复模式绑定标识符的需要。这里使用的策略是扫描模式以查找标识符,并使用该标识符构建一个顶层的 let 语句,该语句内部使用 match 来应用模式。这种扫描几乎总是可能的——请参见下文中的限制 #1 和 #2。

这种策略还意味着 PAT 需要以未解析的标记树序列的形式输入到宏中。有两种方法可以接受无界的标记树序列作为输入而不引起歧义错误:将标记树放在末尾(我的当前选择)或将它们放在括号中。最初,这个选择导致了反向调用语法。从版本 0.2.0 开始,通过采用两次解析策略,支持更方便的语法:宏基本上将其整个输入视为标记序列,在 =else 处分割,然后再次解析结果。

在展开过程中存在许多细微之处,以避免各种警告和陷阱;有关更多详细信息,请参阅宏源代码。

限制

  1. 模式中的表达式不支持。这是当前 Rust 宏系统的一个限制——我想说“在这个位置解析一个标识符,如果失败则尝试解析一个表达式”,但这是不可能的;我只能测试 特定 的标识符。可以通过使用模式守卫(如 match)来轻松绕过这个限制。
  2. 空的、无命名空间的枚举变体和结构体会导致展开失败,因为宏认为它们是标识符。尽管如此,也可以绕过这个限制,尽管有一个公开的 PR 旨在移除最简单的解决方案
    • 对于空的枚举变体,在 #29383 转换为错误之前,请使用 Empty(..),之后将枚举名称作为 Enum::Empty 包含进来。(现在你将收到警告。)
    • 对于类似单位的结构体,在 #29383 转换为错误之前,请使用 Empty(..),之后将其命名空间为 namespace::Empty,或者使用 Empty{}(需要 #![feature(braced_empty_structs)])。(现在你将收到警告。)
    • 当然,您也可以使用路径来引用变体或结构体,尽管这可能不可能(如果它是函数/块的局部变量)或者不方便(如果它是从其他模块或crate导入的)。
  3. PAT不能被推翻。这与if letmatch的行为相同,而且无论如何编写一个带有不可推翻模式的守卫也是无用的(你只需要使用let),所以这不应该是一个问题。由于限制#1,这可能会比本来更令人烦恼。尽管如此,如果#14252得到修复,可以通过在展开中插入一个空操作模式守卫来允许不可推翻的模式。

rustc实现的差异

  • 不需要在或模式周围使用括号
  • 不能将ref绑定到非可复制的值(你会得到一个borrowck错误)

没有运行时依赖

特性