6个版本

0.2.0 2020年4月12日
0.1.5 2016年8月20日
0.1.3 2016年4月11日
0.1.2 2016年2月29日
0.1.0 2015年12月7日

#991进程宏

MPL-2.0 许可证

8KB
64

Try-Let

Latest Version Documentation Build Status

这是一个类似于在RFC #1303中提出的try-let实现的进程宏。

注意: 语句位置的进程宏目前是不稳定的,这意味着在PR #68717 合并之前,此宏在稳定版Rust上不会工作。

有关更多详细信息,请参阅文档。


lib.rs:

这是一个类似于在RFC #1303中提出的try-let实现的进程宏。

注意: 语句位置的进程宏目前是不稳定的,这意味着在PR #68717 合并之前,此宏在稳定版Rust上不会工作。

用法

try-let是通过进程宏实现的,因为像try-let需要的那样解析模式表达式,使用macro_rules!宏是不可能的。

此插件目前需要启用#![feature(proc_macro_hygiene)],如下所示

#![feature(proc_macro_hygiene)]
use try_let::try_let;

实际使用与let表达式非常相似

try_let!(Some(x) = foo else {
    return Err("Shoot! There was a problem!")
});

else之后的表达式必须发散(例如,通过returncontinuebreakpanic!)。

这也可以处理比SomeNone更复杂的类型

enum E {
    A(i32, i32, i32, i32, Option<i32>, Result<(), i32>),
    B,
}

let foo = E::A(0, 21, 10, 34, Some(5), Err(32));

try_let!(E::A(a, 21, c, 34, Some(e), Err(f)) = foo else {
    unreachable!()
});
// a, c, e, and f are all bound here.
assert_eq!(a, 0);
assert_eq!(c, 10);
assert_eq!(e, 5);
assert_eq!(f, 32);

为什么

这提供了一个简单的方法来避免在Rust中进行大量失效模式匹配的逻辑向右移动。这允许主逻辑流程继续而无需增加缩进级别,同时用发散逻辑处理错误。

如何

一个try_let!()调用展开为以下内容

try_let!(Some(x) = foo else {
    return Err("Shoot! There was a problem!");
});
// ... becomes ...
let (x,) = match foo {
    Some(x) => (x,),
    _ => {
        return Err("Shoot! There was a problem!");
    }
};

关于None和空枚举变体的说明

现在有些人可能会问的一个问题是,像 None 这样的枚举变体是如何处理的?

try_let!(None = foo else {
    return;
});
// ... becomes ...
let () = match foo {
    None => (),
    _ => {
        return;
    }
};

None 并不会被 try-let 误认为是绑定变量,因为 try-let 用来工作的一个不大不小的技巧是:它依赖于 Rust 的风格约定。解析器(语法扩展所能访问的所有内容)无法确定模式中的单独标识符是一个像 None 这样的空枚举变体,还是一个像 x 这样的变量绑定。这个决定是在编译器中稍后进行的,对于这个扩展来说太晚了,无法使用这个信息。

相反,扩展会检查标识符的第一个字符。如果它是一个 ASCII 大写字母,我们就假设它是一个空枚举变体;否则,我们假设它是一个变量绑定。

依赖关系

~1.5MB
~35K SLoC