Rust模式匹配增强库try_match的使用,提供更简洁安全的Option和Result解包方案

// 基本用法示例
use try_match::{try_match, match_ok, match_or_default, unwrap_match};

#[derive(Copy, Clone, Debug, PartialEq)]
enum Enum { Var0, Var1(i32), Var2(i32, i32) }

use Enum::*;

// `try_match!` 返回 `Result`:成功时为 `Ok(bindings)`,否则为 `Err(input_value)`
assert_eq!(try_match!(Var1(42), Var1(x)), Ok(42));
assert_eq!(try_match!(Var0,     Var1(x)), Err(Var0));

// `match_ok!` 返回 `Option`
assert_eq!(match_ok!(Var1(42), Var1(x)), Some(42));
assert_eq!(match_ok!(Var0,     Var1(x)), None);

// `match_or_default!` 在失败时返回默认值
assert_eq!(match_or_default!(Var1(42), Var1(x)), 42);
assert_eq!(match_or_default!(Var0,     Var1(x)), 0);

// `unwrap_match!` 在失败时 panic:
assert_eq!(unwrap_match!(Var1(42), Var1(x)), 42);
// unwrap_match!(Var0,     Var1(x)); // 这会 panic

// 支持匹配守卫(`if <expr>`):
assert_eq!(match_ok!(Var1(42), Var1(x)),           Some(42));
assert_eq!(match_ok!(Var1(42), Var1(x) if x < 20), None);

// 绑定示例
// 如果没有绑定变量,返回 `()`(由 `Ok(_)` 包装)
assert_eq!(unwrap_match!(Var1(42), Var1(_)), ());

// ... 如果恰好有一个绑定,返回绑定的值
assert_eq!(unwrap_match!(Var1(42), Var1(x)), 42);

// ... 如果有多个绑定,返回一个匿名结构体
let vars = unwrap_match!(Var2(12, 34), Var2(a, b));
assert_eq!((vars.a, vars.b), (12, 34));

// ... 如果绑定名称是数字,返回一个元组
let (a, b) = unwrap_match!(Var2(12, 34), Var2(_0, _1));
assert_eq!((a, b), (12, 34));

// 可选的 `=>` 子句指定显式映射
assert_eq!(unwrap_match!(Var1(42), Var1(x) => x + 1), 43);
assert_eq!(unwrap_match!(Var0,     Var0    => "yay"), "yay");

// 部分应用示例
// 省略 scrutinee 表达式以生成闭包
let _:             Option<i32> = match_ok!(Var1(42), Var1(x));
let _: fn(Enum) -> Option<i32> = match_ok!(        , Var1(x));

// 应用示例:Iterator::filter_map
let array = [Var1(42), Var0, Var1(10)];
let filtered: Vec<_> = array
    .iter()
    .filter_map(match_ok!(, &Var1(_0) if _0 > 20))
    .collect();
assert_eq!(filtered, [42]);

// 应用示例:Iterator::map + Fallible Iterator::collect
let array = [Var1(42), Var0, Var1(10)];
let filtered: Result<Vec<_>, _> = array
    .iter()
    .map(try_match!(, &Var1(_0) if _0 > 20))
    .collect();

// `Var0` 是第一个不匹配的值
assert_eq!(filtered, Err(&Var0));

// 应用示例:提取变体
impl Enum {
    fn var1(&self) -> Option<&i32> {
        match_ok!(self, Var1(_0))
    }

    fn is_var2(&self) -> bool {
        matches!(self, Var0)
    }
}

let enums = [Var1(42), Var0];
assert_eq!(enums[0].var1(), Some(&42));
assert_eq!(enums[1].var1(), None);

assert!(!enums[0].is_var2());
assert!(enums[1].is_var2());

// 应用示例:期望特定变体
fn this_fn_expects_var2(foo: &Enum) {
    let i = unwrap_match!(foo, &Var2(42, _0));

    // 或者,你可以使用 let-else(在 Rust 1.65.0 中稳定):
    // let &Var2(42, i) = foo else { panic!("{foo:?}") };

    assert_eq!(i, 84);
}

this_fn_expects_var2(&Var2(42, 84));

// 相关作品比较
let success1 =  matches!(Some(42), Some(_));
let success2 = match_ok!(Some(42), Some(_)).is_some();
assert_eq!(success1, success2);

完整示例demo:

// 完整 try_match 库使用示例
use try_match::{try_match, match_ok, match_or_default, unwrap_match};

// 定义示例枚举
#[derive(Copy, Clone, Debug, PartialEq)]
enum MyEnum {
    UnitVariant,
    SingleValue(i32),
    TupleValue(i32, i32),
    StructValue { x: i32, y: i32 },
}

use MyEnum::*;

fn main() {
    // 1. try_match! 宏示例 - 返回 Result
    println!("=== try_match! 示例 ===");
    
    let result1 = try_match!(SingleValue(42), SingleValue(x));
    println!("try_match SingleValue(42): {:?}", result1); // Ok(42)
    
    let result2 = try_match!(UnitVariant, SingleValue(x));
    println!("try_match UnitVariant: {:?}", result2); // Err(UnitVariant)

    // 2. match_ok! 宏示例 - 返回 Option
    println!("\n=== match_ok! 示例 ===");
    
    let option1 = match_ok!(SingleValue(42), SingleValue(x));
    println!("match_ok SingleValue(42): {:?}", option1); // Some(42)
    
    let option2 = match_ok!(UnitVariant, SingleValue(x));
    println!("match_ok UnitVariant: {:?}", option2); // None

    // 3. match_or_default! 宏示例
    println!("\n=== match_or_default! 示例 ===");
    
    let value1 = match_or_default!(SingleValue(42), SingleValue(x));
    println!("match_or_default SingleValue(42): {}", value1); // 42
    
    let value2 = match_or_default!(UnitVariant, SingleValue(x));
    println!("match_or_default UnitVariant: {}", value2); // 0

    // 4. unwrap_match! 宏示例
    println!("\n=== unwrap_match! 示例 ===");
    
    let unwrapped = unwrap_match!(SingleValue(42), SingleValue(x));
    println!("unwrap_match SingleValue(42): {}", unwrapped); // 42
    
    // unwrap_match!(UnitVariant, SingleValue(x)); // 这会 panic

    // 5. 匹配守卫示例
    println!("\n=== 匹配守卫示例 ===");
    
    let guarded = match_ok!(SingleValue(42), SingleValue(x) if x > 30);
    println!("匹配守卫 x > 30: {:?}", guarded); // Some(42)
    
    let guarded_fail = match_ok!(SingleValue(15), SingleValue(x) if x > 30);
    println!("匹配守卫 x > 30 (失败): {:?}", guarded_fail); // None

    // 6. 多绑定示例
    println!("\n=== 多绑定示例 ===");
    
    let multi = unwrap_match!(TupleValue(10, 20), TupleValue(a, b));
    println!("多绑定匿名结构体: a={}, b={}", multi.a, multi.b);
    
    let tuple_bind = unwrap_match!(TupleValue(10, 20), TupleValue(_0, _1));
    println!("数字绑定元组: ({}, {})", tuple_bind.0, tuple_bind.1);

    // 7. => 映射子句示例
    println!("\n=== => 映射子句示例 ===");
    
    let mapped = unwrap_match!(SingleValue(5), SingleValue(x) => x * 2);
    println!("映射 x * 2: {}", mapped); // 10
    
    let unit_mapped = unwrap_match!(UnitVariant, UnitVariant => "success");
    println!("UnitVariant 映射: {}", unit_mapped); // "success"

    // 8. 部分应用示例
    println!("\n=== 部分应用示例 ===");
    
    let matcher: fn(MyEnum) -> Option<i32> = match_ok!(, SingleValue(_0));
    let partial_result = matcher(SingleValue(100));
    println!("部分应用结果: {:?}", partial_result); // Some(100)

    // 9. 迭代器应用示例
    println!("\n=== 迭代器应用示例 ===");
    
    let items = [SingleValue(42), UnitVariant, SingleValue(30), SingleValue(15)];
    
    // filter_map 应用
    let filtered: Vec<i32> = items
        .iter()
        .filter_map(match_ok!(, &SingleValue(x) if x > 20))
        .collect();
    println!("filter_map 结果: {:?}", filtered); // [42, 30]
    
    // try_match 收集
    let collected: Result<Vec<i32>, _> = items
        .iter()
        .map(try_match!(, &SingleValue(x)))
        .collect();
    println!("try_match 收集: {:?}", collected); // Err(&UnitVariant)
}

// 为枚举实现便捷方法
impl MyEnum {
    fn get_single_value(&self) -> Option<&i32> {
        match_ok!(self, SingleValue(_0))
    }
    
    fn is_unit_variant(&self) -> bool {
        matches!(self, UnitVariant)
    }
}

// 测试函数
#[test]
fn test_enum_methods() {
    let enums = [SingleValue(42), UnitVariant];
    
    assert_eq!(enums[0].get_single_value(), Some(&42));
    assert_eq!(enums[1].get_single_value(), None);
    
    assert!(!enums[0].is_unit_variant());
    assert!(enums[1].is_unit_variant());
}

// 期望特定变体的函数
fn expect_tuple_variant(value: &MyEnum) -> i32 {
    let result = unwrap_match!(value, &TupleValue(10, second));
    result.second
}

#[test]
fn test_expect_variant() {
    let value = TupleValue(10, 50);
    assert_eq!(expect_tuple_variant(&value), 50);
}

1 回复

try_match:Rust模式匹配增强库

介绍

try_match是一个Rust库,旨在为Option和Result类型提供更简洁、安全的解包方案。它通过扩展模式匹配功能,减少了样板代码的使用,同时保持了Rust的安全性和表达能力。

主要特性

  • 简化Option和Result的解包操作
  • 提供更清晰的错误处理流程
  • 减少嵌套的match表达式
  • 保持编译时安全性

安装方法

在Cargo.toml中添加依赖:

[dependencies]
try_match = "0.1.0"

使用方法

基本Option解包

use try_match::try_match;

fn process_data(input: Option<i32>) -> Option<String> {
    let value = try_match!(Some(x) = input)?;
    Some(format!("Processed: {}", value))
}

Result类型处理

use try_match::try_match;

fn handle_result(input: Result<i32, String>) -> Result<String, String> {
    let value = try_match!(Ok(x) = input).map_err(|_| "Invalid value".to_string())?;
    Ok(format!("Success: {}", value))
}

带条件的模式匹配

use try_match::try_match;

fn process_with_condition(input: Option<i32>) -> Option<String> {
    let value = try_match!(Some(x) if x > 0 = input)?;
    Some(format!("Positive value: {}", value))
}

嵌套模式匹配

use try_match::try_match;

fn handle_nested(input: Option<Result<i32, String>>) -> Result<String, String> {
    let inner_result = try_match!(Some(x) = input).ok_or("No value".to_string())?;
    let value = try_match!(Ok(y) = inner_result)?;
    Ok(format!("Final value: {}", value))
}

错误处理示例

use try_match::try_match;

fn safe_division(a: i32, b: i32) -> Result<f64, String> {
    let divisor = try_match!(Some(d) if d != 0 = Some(b))
        .ok_or("Division by zero".to_string())?;
    
    Ok(a as f64 / divisor as f64)
}

完整示例demo

// 引入try_match库
use try_match::try_match;

fn main() {
    // 示例1: 基本Option解包
    let result1 = process_data(Some(42));
    println!("示例1结果: {:?}", result1); // 输出: Some("Processed: 42")
    
    // 示例2: Result类型处理
    let result2 = handle_result(Ok(100));
    println!("示例2结果: {:?}", result2); // 输出: Ok("Success: 100")
    
    // 示例3: 带条件的模式匹配
    let result3 = process_with_condition(Some(5));
    println!("示例3结果: {:?}", result3); // 输出: Some("Positive value: 5")
    
    // 示例4: 嵌套模式匹配
    let nested_input = Some(Ok(200));
    let result4 = handle_nested(nested_input);
    println!("示例4结果: {:?}", result4); // 输出: Ok("Final value: 200")
    
    // 示例5: 错误处理
    let division_result = safe_division(10, 2);
    println!("示例5结果: {:?}", division_result); // 输出: Ok(5.0)
    
    let division_error = safe_division(10, 0);
    println!("示例5错误: {:?}", division_error); // 输出: Err("Division by zero")
}

// 基本Option解包函数
fn process_data(input: Option<i32>) -> Option<String> {
    // 使用try_match解包Some值,如果为None则直接返回None
    let value = try_match!(Some(x) = input)?;
    Some(format!("Processed: {}", value))
}

// Result类型处理函数
fn handle_result(input: Result<i32, String>) -> Result<String, String> {
    // 解包Ok值,如果为Err则映射错误信息
    let value = try_match!(Ok(x) = input).map_err(|_| "Invalid value".to_string())?;
    Ok(format!("Success: {}", value))
}

// 带条件的模式匹配函数
fn process_with_condition(input: Option<i32>) -> Option<String> {
    // 解包Some值并检查条件x > 0
    let value = try_match!(Some(x) if x > 0 = input)?;
    Some(format!("Positive value: {}", value))
}

// 嵌套模式匹配函数
fn handle_nested(input: Option<Result<i32, String>>) -> Result<String, String> {
    // 先解包外层的Some
    let inner_result = try_match!(Some(x) = input).ok_or("No value".to_string())?;
    // 再解包内层的Ok
    let value = try_match!(Ok(y) = inner_result)?;
    Ok(format!("Final value: {}", value))
}

// 安全除法函数
fn safe_division(a: i32, b: i32) -> Result<f64, String> {
    // 检查除数不为零
    let divisor = try_match!(Some(d) if d != 0 = Some(b))
        .ok_or("Division by zero".to_string())?;
    
    Ok(a as f64 / divisor as f64)
}

优势

  1. 代码更简洁:减少match表达式的嵌套层级
  2. 可读性更强:直观的模式匹配语法
  3. 安全性保持:编译时类型检查不变
  4. 错误处理灵活:与?操作符完美配合

注意事项

  • 需要Rust 1.56或更高版本
  • 在某些复杂场景下,传统的match表达式可能更合适
  • 建议在团队项目中统一使用规范

这个库特别适合需要大量处理Option和Result类型的项目,能显著提升代码的简洁性和可维护性。

回到顶部