#match #value #defines

fn_match

定义一种基于将每个值传递给函数的新类型的 match

1 个不稳定版本

0.1.0 2022 年 11 月 24 日

#60#defines

WTFPL 许可证

9KB

定义另一种类型的 match 语句。

与将单个值与多个模式进行比较不同,fn_match 将每个值传递给一个给定的函数,并根据哪个情况导致有利的结果来评估表达式。

与传统的 match 可以像一系列 if x == y 为各种 y 行为一样,fn_match 可以像(并展开为)一系列 if let Some(x) = foo(bar) 为各种 bar

示例

快速入门

let x = fn_match! {
    with fn: foo => y;
    "bar" => baz(y),
    "baz" => bar(y)
};

展开为

let x = if let Some(y) = foo("bar") {
    Some(baz(y))
} else if let Some(y) = foo("baz") {
    Some(bar(y))
} else {
    None
};

详细示例

一个用例(也是主要用例)是当使用具有互斥捕获组的 Regex 时。

let regex = Regex::new(r#"^(?:(?P<int>\d+)|(?P<str>".+")|(?P<flt>\d+\.\d+))$"#).unwrap();
#
#
#

此正则表达式可以捕获一个整型字面量(235)、一个字符串字面量("hello")或一个(无符号)浮点字面量(87.43),并可用于生成以下枚举的实例

enum Token {
    Int(u64),
    Str(String),
    Float(f64)
}

在标准 Rust 中,从给定的 &str 生成 Token 的代码可能如下所示

let input = r#""hello world""#;
let caps = regex.captures(input).expect("failed to match");

let token = if let Some(tok) = caps.name("int").map(|s| s.as_str()) {
    Token::Int(tok.parse().unwrap())
} else if let Some(tok) = caps.name("str").map(|s| s.as_str()) {
    Token::Str(tok.trim_matches('\"').to_string())
} else if let Some(tok) = caps.name("flt").map(|s| s.as_str()) {
    Token::Float(tok.parse().unwrap())
} else { panic!("failed to match") };

assert_eq!(token, Token::Str(String::from("hello world")));

应用 fn_match 以及额外的闭包,可以减少重复,并将代码重写为以下内容

let input = r#"42"#;
let caps = regex.captures(input).expect("failed to match");

let f = |g| caps.name(g).map(|s| s.as_str());
let token = fn_match! {
    with fn: f => tok;
    "int" => Token::Int(tok.parse().unwrap()),
    "str" => Token::Str(tok.trim_matches('\"').to_string()),
    "flt" => Token::Float(tok.parse().unwrap())
}.expect("failed to match");

assert_eq!(token, Token::Int(42));

fn_match 语法

fn_match 块有三个主要部分

  • 要调用的函数
  • 一个标识符
  • 一系列的左右表达式对
#
    with fn: f => tok;
#

此行提供了一个函数和一个标识符。该函数(位于 fn:=> 之间)需要接受一个类型,该类型是后续行提供的单个值,并返回一个 Option

注意:函数表达式放置在每个if let尝试之后进行展开。如果函数是闭包,建议在fn_match之前将其存储在变量中,以防止在最终代码中写入多个相同的闭包。

标识符(在=>;之间)用于引用包含的Some值,如果函数返回该值。(它用作if let Some(x))的绑定模式。

在此行之后是一系列匹配对。

#
    "int" => Token::Int(tok.parse().unwrap()),
#

=>的左侧是测试值,需要传递到之前提供的函数中。

右侧是一个要评估/返回的表达式(在它自己的Some中包装),如果函数使用左侧值调用时返回Some。右侧也可以引用标识符作为相关的Some值。

整个fn_match块表达式评估为Option。要么是一个包含左侧匹配的右侧的Some,要么是如果没有左侧匹配,则返回None

无运行时依赖