Rust 模式匹配

66 阅读18分钟

Rust 模式匹配

1.模式匹配的基本概念

在 Rust 中,模式匹配(Pattern Matching)是一种强大的语言特性,用于对数据进行结构化的比较和解构。它允许你根据数据的形状(如枚举、结构体、元组等)进行匹配,并从中提取出需要的值。模式匹配的核心是match表达式和if let语句,它们提供了灵活且高效的方式来处理各种数据结构。

模式的类型

• 变量模式:绑定值到变量,如x

• 字面量模式:匹配具体的值,如1true"hello"

• 结构体模式:解构结构体字段,如Point { x, y }

• 枚举模式:匹配枚举变体,如Some(x)None

• 元组模式:解构元组元素,如(a, b)

• 范围模式:匹配一个范围内的值,如1..=10

• 通配符模式:匹配任何值,用_表示。

变量模式是一种基础模式,而其他模式(如字面量模式、结构体模式、枚举模式、元组模式、范围模式、通配符模式等)是 Rust 中独立的模式类型,它们各自有特定的用途和语法。不过,这些模式类型在某些情况下可以与变量模式结合使用,或者在功能上与变量模式有交集。

2.match 表达式

match是 Rust 中最常用的模式匹配工具。它允许你根据一个表达式的值选择执行不同的代码分支。

语法

match expression {
    pattern1 => block1,
    pattern2 => block2,
    ...
    _ => default_block, // 默认分支,可选
}

• expression:要匹配的值。

2.1 变量模式

含义: 变量模式是最基本的模式类型,它将匹配到的值绑定到一个变量中。变量模式的作用是捕获值,以便在匹配的代码块中使用。

fn main() {
    let num = 5;
​
    match num {
        x => println!("The number is {}", x),
    }
}

2.2 字面量模式

含义: 字面量模式用于匹配具体的值,而不是捕获值。它与变量模式不同,因为字面量模式不会绑定值到变量,而是直接匹配某个具体的值。

fn main() {
    let num = 5;
​
    match num {
        5 => println!("The number is five"),
        _ => println!("The number is not five"),
    }
}

2.3 结构体模式

含义: 结构体模式用于解构结构体的字段,并可以结合变量模式将字段的值绑定到变量中。

fn main() {
    let point = Point { x: 1, y: 2 };
​
    match point {
        Point { x, y } => println!("Point is ({}, {})", x, y),
    }
}
struct Point {
    x: i32,
    y: i32,
}

2.4 枚举模式

含义: 枚举模式用于匹配枚举的变体,并可以结合变量模式捕获变体中的值。

fn main() {
    let favourite_fruit = Fruit::Grape;
​
    match favourite_fruit {
        //在枚举模式匹配中, _可以用来忽略变体中的值。
        Fruit::Apple { origin, price: _ } => println!("the fruit is apple, color is red, origin is {}", origin),
        Fruit::Watermelon(_, _, _) => println!("the watermelon"),
        //用于表示 或 关系,用于将多个模式组合在一起。
        Fruit::Banana | Fruit::Pear => println!("the fruit color is yellow"),
        Fruit::Orange => println!("the fruit is orange, color is orange"),
        _ => println!("the fruit is grape, color is purple"),
    }
}
​
enum Fruit {
    Apple{origin:String,price:u16},
    Banana,
    Orange,
    Grape,
    Pear,
    Watermelon(i32,i32,i32),
}

在这个例子中,Message::Move { x, y } 是枚举模式,它匹配了 Message::Move 变体,并将 x 和 y 的值绑定到变量 x 和 y 中。

  • _可以用来忽略变体中的值。
  • | 用于表示 或 关系,用于将多个模式组合在一起。

2.5 元组模式

含义: 元组模式用于解构元组的元素,并可以结合变量模式捕获这些元素的值。

fn main() {
    let point = (1, 2);
​
    match point {
        (x, y) => println!("Point is ({}, {})", x, y),
    }
}

在这个例子中,(x, y) 是元组模式,它解构了 point 的两个元素,并将它们分别绑定到变量 x 和 y 中。

2.6 范围模式

含义: 范围模式用于匹配一个值是否在某个范围内。它本身不绑定值到变量,但可以与变量模式结合使用。

fn main() {
    let num = 5;
​
    match num {
        1..=3 => println!("1 to 3"),
        4..=6 => println!("4 to 6"),
        _ => println!("Other"),
    }
}
​

2.7 通配符模式

含义: 通配符模式 _ 用于匹配任何值,但不会绑定值到变量。它通常用作默认分支。

fn main() {
    let num = 5;
​
    match num {
        5 => println!("The number is five"),
        _ => println!("The number is not five"),
    }
​
}
​

2.8 其他

2.8.1 使用match表达式赋值

match 本身也是一个表达式,因此可以用它来赋值:

fn main() {
    let ip1 = IpAddr::Ipv4;
    let ip_str = match ip1 {
        IpAddr::Ipv4 => "127.0.0.1",
        _ => "::1",
    };
​
    println!("{}", ip_str);
}
​
enum IpAddr {
    Ipv4,
    Ipv6
}
2.8.2 解构嵌套的结构体和枚举
enum Color {
    Rgb(i32, i32, i32),
    Hsv(i32, i32, i32),
}
​
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(Color),
}
​
fn main() {
    let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));
​
    match msg {
        Message::ChangeColor(Color::Rgb(r, g, b)) => {
            println!(
                "Change the color to red {}, green {}, and blue {}",
                r,
                g,
                b
            )
        }
        Message::ChangeColor(Color::Hsv(h, s, v)) => {
            println!(
                "Change the color to hue {}, saturation {}, and value {}",
                h,
                s,
                v
            )
        }
        _ => ()
    }
}
2.8.3 解构数组

对于数组的解构,可以用类似元组的方式解构,分为两种情况:

定长数组

fn main() {
    let arr: [u16; 2] = [114, 514];
    let [x, y] = arr;
    assert_eq!(x, 114);
    assert_eq!(y, 514);
}

不定长数组

fn main() {
    //定义了一个u16类型的数组切片arr
    let arr: &[u16] = &[114, 514];
​
    // [x, ..]表示匹配切片的第一个元素x,并忽略后续的所有元素(..表示“剩余部分”)
    // x是一个引用,指向切片的第一个元素。
    // assert_eq!(x, &114)断言x等于&114,确保匹配正确。
    if let [x, ..] = arr {
        assert_eq!(x, &114);
    }
    // &[.., y]表示匹配切片的最后一个元素y,并忽略前面的所有元素。
    // y是一个值,而不是引用,因为&已经被显式地解引用。
    // assert_eq!(y, 514)断言y等于514,确保匹配正确
    if let &[.., y] = arr {
        assert_eq!(y, 514);
    }
​
    // 定义一个空切片
    let arr: &[u16] = &[];
    //空切片匹配 [..],因为 [..] 表示任何切片
    assert!(matches!(arr, [..]));
    //空切片不匹配 [x, ..],因为 [x, ..] 要求至少有一个元素
    assert!(!matches!(arr, [x, ..]));
}

3.if let 语句

if let 是 Rust 中一种非常简洁的模式匹配语法,适用于只关心某个特定模式的情况。它支持多种模式类型(如Option、Result、枚举、结构体、元组等),并且可以结合模式守卫和 | 操作符使用。if let 的主要优势在于减少冗余代码和提高可读性,但它不提供 match 的穷尽性检查。

3.1 语法

if let pattern = expression {
    // 如果模式匹配成功,执行这里的代码
} else {
    // 如果模式匹配失败,执行这里的代码(可选)
}
​

• pattern:需要匹配的模式。

• expression:要匹配的值。

• else:可选的分支,用于处理模式匹配失败的情况。

3.2 示例

fn main() {
​
    //匹配Option类型 只关心Some的情况,而忽略None
    let option = Some(3);
    if let Some(value) = option {
        println!("Got a value: {}", value);
    } else {
        println!("Got None");
    }
​
    //匹配Result类型 只关心Ok的情况,而忽略Err
    let result: Result<i32, &str> = Ok(20);
    if let Ok(value) = result {
        println!("Matched Ok with value: {}", value);
    } else {
        println!("Matched Err");
    }
​
    //匹配枚举 可以用于匹配任何枚举类型,只关心某个特定变体
    let msg = Message::Move { x: 1, y: 2 };
    if let Message::Move { x, y } = msg {
        println!("Moving to ({}, {})", x, y);
    } else {
        println!("Not a Move message");
    }
​
    //匹配结构体 可以用于匹配结构体的字段
    let point = Point { x: 1, y: 2 };
    if let Point { x, y } = point {
        println!("Point is ({}, {})", x, y);
    }
​
    //匹配元组 可以用于匹配元组的元素
    let point = (1, 2);
    if let (x, y) = point {
        println!("Point is ({}, {})", x, y);
    }
​
​
    //使用模式守卫 可以结合模式守卫使用,进一步检查模式匹配的值是否满足某些条件
    let optional = Some(10);
    if let Some(value) = optional {
        if value > 5 {
            println!("Value is greater than 5: {}", value);
        }
    } else {
        println!("No value");
    }
​
    //嵌套if let 可以嵌套使用,处理更复杂的情况
    let numbers = vec![Some(1), None, Some(3)];
    for num in &numbers {
        if let Some(value) = num {
            if value % 2 == 0 {
                println!("Found an even number: {}", value);
            } else {
                println!("Found an odd number: {}", value);
            }
        } else {
            println!("Found a None");
        }
    }
    
}
​
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}
​
struct Point {
    x: i32,
    y: i32,
}
​

3.3 错误一 模式必须绑定相同的变量

fn main() {
    //使用|操作符 支持使用|操作符来匹配多个模式。
    let msg = Message::Move { x: 1, y: 2 };
    if let Message::Move { x, y } | Message::Write(_) = msg {
        println!("Message is either Move or Write");
    } else {
        println!("Message is Quit");
    };
}
​
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}
​
报错信息:  error[E0408]: variable `x` is not bound in all patterns

这个错误发生的原因是,在if let或match表达式中,当你使用|来组合多个模式时,所有模式必须绑定相同的变量。如果某些模式没有绑定这些变量,就会导致这个错误。

• Message::Move { x, y } 绑定了变量 x 和 y 。

• Message::Write(_)没有绑定变量 x 和 y 。因此,编译器会报错,提示变量 x 和 y 在所有模式中没有被一致地绑定。

解决方法

  1. 确保所有模式都绑定相同的变量。
  2. 使用单独的 if let 处理不同的模式。
  3. 使用 match 替代 if let,以处理更复杂的逻辑。
fn main() {
    let msg = Message::Move { x: 1, y: 2 };
​
    // 使用单独的 if let
    if let Message::Move { x, y } = msg {
        println!("Moving to ({}, {})", x, y);
    } else if let Message::Write(text) = msg {
        println!("Writing text: {}", text);
    } else {
        println!("Other message");
    }
​
    let msg = Message::Move { x: 1, y: 2 };
​
    // 使用 match
    match msg {
        Message::Move { x, y } => println!("Moving to ({}, {})", x, y),
        Message::Write(text) => println!("Writing text: {}", text),
        _ => println!("Other message"),
    }
}
​
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}
​

rust中为什么要设计模式必须绑定相同的变量

match msg {
    Message::Move { x, y } | Message::Write(_) => println!("x is {}", x),
    _ => println!("Other"),
}

在这个例子中,Message::Move { x, y } 绑定了变量 x 和 y ,但 Message::Write(_) 没有绑定 x 和 y 。如果 Rust 允许这种写法,那么在匹配 Message::Write 时, x 和 y 将没有定义,导致编译错误或运行时错误。通过要求所有模式绑定相同的变量,Rust 避免了这种潜在的错误。

3.4 while let 条件循环

一个与 if let 类似的结构是 while let 条件循环,它允许只要模式匹配就一直进行 while 循环。下面展示了一个使用 while let 的例子:

fn main() {
    let mut stack = Vec::new();
​
    //向数组尾部插入元素
    stack.push(1);
    stack.push(2);
    stack.push(3);
​
    // pop 方法取出动态数组的最后一个元素并返回Some(value),
    // 如果动态数组是空的,将返回 None,对于 while 来说,只要 pop 返回 Some 就会一直不停的循环。
    // 一旦其返回 None,while 循环停止。我们可以使用 while let 来弹出栈中的每一个元素。
    while let Some(top) = stack.pop() {
        println!("{}", top);
    };
}

4.mathces!宏

matches! 是 Rust 标准库中的一个宏,用于检查一个表达式是否匹配某个模式。它的语法和行为类似于 match 表达式,但更简洁,通常用于简单的模式匹配检查。

4.1 语法

rustmatches!(expression, pattern)

• expression:要匹配的值。

• pattern:模式,用于匹配expression。

matches!宏返回一个布尔值:

• 如果expression匹配pattern,返回true。

• 否则,返回false。

4.2 示例

fn main() {
    //简单匹配
    let foo = 'f';
    // 检查 foo 是否匹配 'A' 到 'Z' 或 'a' 到 'z'
    assert!(matches!(foo,'A'..='Z' | 'a'..='z'));
​
    //使用模式守卫
    let bar = Some(4);
    // 检查 bar 是否匹配 Some(x),并且 x > 2
    assert!(matches!(bar,Some(x) if x> 2));
​
    //检查枚举变体
    let msg = Message::Move { x: 1, y: 2 };
    assert!(matches!(msg,Message::Move {x,y} if x>0));
​
    //检查数组或切片
    let arr: &[u16] = &[114, 514];
    // 检查 arr 是否匹配 [x, ..],即是否有至少一个元素
    assert!(matches!(arr, [x, ..]));
    // 检查 arr 是否匹配 &[.., y],即是否有至少一个元素
    assert!(matches!(arr, &[.., y]));
​
    //检查空切片
    let arr: &[u16] = &[];
    // 检查 arr 是否匹配 [..],即是否是任意切片(包括空切片)
    assert!(matches!(arr, [..]));
    // 检查 arr 是否匹配 [x, ..],即是否有至少一个元素
    assert!(!matches!(arr, [x, ..]));
}
​
enum Message{
    Quit,
    Move{x:i32,y:i32},
    Write(String)
}

4.3 实现原理

#[macro_export] //这个属性表示这个宏可以在定义它的 crate 之外使用。
#[stable(feature = "matches_macro", since = "1.42.0")] //这个属性表示这个宏是稳定的,并且从 Rust 1.42.0 版本开始可用。
#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")] //这个属性是条件编译属性,用于在非测试模式下为宏添加一个诊断项。
macro_rules! matches {
    ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
        match $expression {
            $pattern $(if $guard)? => true,
            _ => false
        }
    };
}

(expression:expr,expression:expr, pattern:pat (if(if guard:expr)? $(,)?)

这是宏的参数模式,定义了宏可以接受的参数类型。

$expression:expr :表示宏的第一个参数是一个表达式。

$pattern:pat :表示宏的第二个参数是一个模式。

$(if $guard:expr)? :表示宏可以接受一个可选的模式守卫( if 条件)。

$(,)? :表示宏可以接受一个可选的逗号,这使得宏的调用可以以逗号结尾

match expression { pattern (if(if guard)? => true, _ => false }

宏展开后的代码是一个match表达式。

match $expression { ... }:对$expression进行模式匹配。

$pattern $(if $guard)? => true :如果 expression匹配expression 匹配 pattern 并且满足可选的 $guard 条件,则返回 true 。

_ => false :如果 expression不匹配expression 不匹配 pattern 或不满足 $guard 条件,则返回 false。

fn main() {
    let msg = Message::Move { x: 1, y: 2 };
    matches!(msg,Message::Move {x,y} if x>0);
​
    //$expression:expr 是 msg
    //$pattern:pat 是 Message::Move {x,y}
    //$(if $guard:expr)? 是 if x>0
    //所以  matches!(msg,Message::Move {x,y} if x>0); 等价于 如下代码
​
    let bool = match msg {
        Message::Move { x, y } if x > 0 => true,
        _ => false,
    };
}
​
enum Message{
    Quit,
    Move{x:i32,y:i32},
    Write(String)
}

4.4 常用场景

4.4.1 动态数组过滤元素
fn main() {
    let v = vec![MyEnum::Foo, MyEnum::Bar, MyEnum::Foo];
​
    //因为数组里存储的是元素的引用,需要将引用转换为实际的值
    //使用.cloned()或.copied(),实现Clone或Copy特性 可以使用.cloned()或.copied()将引用转换为值。
    //let v: Vec<MyEnum> = v.iter().filter(|x| matches!(x,MyEnum::Foo)).copied().collect();
    //let v: Vec<MyEnum> = v.iter().filter(|x| matches!(x,MyEnum::Foo)).cloned().collect();
    //直接操作值(不使用引用) 使用 into_iter() 获取值 可以不实现Clone或Copy特性
    let v: Vec<MyEnum> = v.into_iter().filter(|x| matches!(x,MyEnum::Foo)).collect();
    println!("v is {:?}", v);
}
#[derive(Debug, Copy, Clone)]
enum MyEnum {
    Foo,
    Bar,
}

5.模式匹配符号

1. _ (通配符)

  • 含义: 匹配任何值,但不会绑定该值到任何变量。
  • 用途: 作为默认分支或忽略某些值。
match x {
    1 => println!("One"),
    _ => println!("Any other value"),
}

2. | (或操作符)

  • 含义: 组合多个模式,表示"或"的关系
  • 用途: 如果任何一个模式匹配成功,就会执行对应的代码块。
match x {
    1 | 2 => println!("One or Two"),
    _ => println!("Other"),
}

3. ..和..= (范围匹配)

  • 含义:匹配一个范围内的值。..代表左开右闭 ..=表示左开右开
  • 用途:用于数值类型或字符类型。
fn main() {
    let x = 4;
    match x {
        1..5 => println!("Between 1 and 4"),
        6..=10 => println!("Between 6 and 10"),
        _ => println!("Other"),
    }
​
    let c = 'c';
​
    match c {
        'a'..='z' => println!("Lowercase letter"), //匹配所有小写字母
        'A'..='Z' => println!("Uppercase letter"), //匹配所有大写字母
        //'a'..='z' | 'A'..='Z' => println!("all"), // 匹配所有字符可以用 |进行组合
        '0'..='9' => println!("Digit"), //匹配所有数字字符。
​
        'Z'..='a' => println!("xxx"),
        _ => println!("Other"),
​
        //不合法的情况
        //'a'..='Z'是不合法的,因为'a'和'Z'的 Unicode 码点顺序是不连续的。'a'码点97 'Z'码点90
        //'a'..='Z' => println!("xxx"),
    }
}
​

4. if(模式守卫)

• 含义:在模式匹配中添加额外的条件判断。

• 用途:用于更复杂的匹配逻辑。

//基本语法
match expression {
    pattern if condition => block,
    ...
}
​
​
fn main() {
    let msg = Message::Move { x: 1, y: 2 };
    match msg {
        Message::Move { x, y } if x > 0 && y > 0 => println!("Moving to ({}, {})", x, y),
        Message::Move { x, y } if x < 0 && y < 0 => println!("Moving to negative coordinates ({}, {})", x, y),
        Message::Write(text) if text.len() > 5 => println!("Writing long text: {}", text),
        Message::Write(text) => println!("Writing short text: {}", text),
        _ => println!("Other message"),
    }
}
​
​
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

为什么叫“模式守卫”?

“模式守卫”这个名字来源于它的功能:它就像一个“守卫”,在模式匹配成功之后,进一步检查是否满足某些条件。如果条件不满足,即使模式匹配成功,也不会执行对应的代码块。因此,它“守卫”了模式匹配的结果,确保只有在满足所有条件时才会执行特定的逻辑。

5. @(绑定)

• 含义:将匹配到的值绑定到一个变量中,同时进行模式匹配。

• 用途:允许在匹配模式的同时将值绑定到一个变量中。这使得你可以在匹配某个模式的同时,保留原始值以便后续使用。

@ 绑定的语法如下:

pattern: variable @ subpattern

• pattern:完整的模式。

• variable:你希望绑定的变量名。

• subpattern:子模式,用于进一步匹配值。

示例

fn main() {
    //枚举中的 @ 绑定
    let msg = Message::Hello { id: 5 };
    match msg {
        //Message::Hello { id: id_variable @ 3..=7 }:将id的值绑定到变量id_variable,并检查它是否在范围3..=7内
        Message::Hello { id: id_variable @ 3..=7 } => {
            println!("Found an id in range:{}", id_variable);
        }
        //Message::Hello { id: 10..=12 }:只匹配范围10..=12但没有绑定变量
        Message::Hello { id: 10..=12 } => {
            println!("Found an id in another range");
        }
        //Message::Hello { id }:匹配任何其他id值,并绑定到变量id
        Message::Hello { id } => {
            println!("Found some other id:{}", id);
        }
    }
    //结构体中的 @ 绑定
    let point = Point { x: 10, y: 20 };
​
    match point {
        //p @ Point { x: 5..=15, y } : 将point绑定到变量p,并检查x是否在范围5..=15内
        p @ Point { x: 5..=15, y } => {
            println!("Point is within x range:{:?}", p);
        }
        //Point { x, y }:匹配任何其他Point,并绑定x和y
        Point{x,y} =>{
            println!("Point is outside x range: ({},{})", x, y);
        }
    }
​
    //元组中的 @ 绑定
    let point = (10,20);
    match point {
        // p @ (5..=15, y):将point绑定到变量p,并检查第一个元素是否在范围5..=15内
        p @ (5..=15, y) => {
            println!("Point is within x range:{:?}", p);
        }
        //(x, y):匹配任何其他元组,并绑定 x 和 y
        (x,y) =>{
            println!("Point is outside x range: ({}, {})", x, y);
        }
    }
​
}
​
enum Message {
    Hello { id: i32 },
}
​
#[derive(Debug)]
struct Point{
    x:i32,
    y:i32,
}

@绑定是 Rust 模式匹配中一个非常强大的特性,它允许你在匹配模式的同时将值绑定到一个变量中。这使得你可以在匹配某个模式的同时,保留原始值以便后续使用。@绑定适用于以下场景:

• 范围匹配并绑定变量:在匹配某个范围的同时,保留原始值。

• 结构体字段解构并绑定:在解构结构体字段的同时,保留整个结构体。

• 枚举变体解构并绑定:在解构枚举变体的同时,保留整个枚举变体。

6. ref 和ref mut(引用绑定)

• 含义:用于匹配引用类型,避免移动所有权。

• 用途: ref 和 ref mut 是用于创建引用绑定的关键字。它们允许你在模式匹配时,将值绑定为引用,而不是直接移动或复制值的所有权。这种特性在处理所有权和借用时非常有用,尤其是在需要避免所有权转移的场景中。

fn main() {
​
    //使用ref避免所有权转移
    //ref y将x绑定为不可变引用,而不是移动所有权。因此,x在匹配后仍然可用。
    let x = String::from("test");
    match x {
        ref y => println!("y is {}", y),
        _ => println!("Other"),
    }
    println!("x is still available: {}", x);
​
    //使用ref mut避免所有权转移
    //ref mut y将x绑定为可变引用,而不是移动所有权。因此,x在匹配后仍然可用,并且可以被修改。
    let mut x = String::from("test");
    match x {
        ref mut y => {
            y.push_str(", world!");
            println!("y is {}", y);
        }
        _ => println!("Other"),
    }
    println!("x is modified: {}", x);
​
    //结构体字段的引用绑定
    //Point { x: ref x_ref, y: ref y_ref }将point的字段x和y绑定为不可变引用。因此,point在匹配后仍然可用。
    let point = Point { x: 1, y: 2 };
    match point {
        Point { x: ref x_ref, y: ref y_ref } => {
            println!("x_ref: {}, y_ref: {}", x_ref, y_ref);
        }
    }
    println!("point: {:?}", point);
​
    //结构体字段的可变引用绑定
    // Point { x: ref mut x_ref, y: ref mut y_ref }将point的字段x和y绑定为可变引用。因此,point在匹配后仍然可用,并且可以被修改。
    let mut point = Point { x: 1, y: 2 };
    match point {
        Point { x: ref mut x_ref, y: ref mut y_ref } => {
            *x_ref += 1;
            *y_ref += 1;
            println!("x_ref: {}, y_ref: {}", x_ref, y_ref);
        }
    }
    println!("point: {:?}", point);
​
    //元组的引用绑定
    //(ref x_ref, ref y_ref)将point的元素绑定为不可变引用,因此,point在匹配后仍然可用。
    let point = (1, 2);
    match point {
        (ref x_ref, ref y_ref) => {
            println!("x_ref: {}, y_ref: {}", x_ref, y_ref);
        }
    }
    println!("point: {:?}", point);
​
    //元组的可变引用绑定
    //(ref mut x_ref, ref mut y_ref)将point的元素绑定为可变引用,因此,point在匹配后仍然可用,并且可以被修改
    let mut point = (1, 2);
    match point {
        (ref mut x_ref, ref mut y_ref) => {
            *x_ref += 1;
            *y_ref += 1;
            println!("x_ref: {}, y_ref: {}", x_ref, y_ref);
        }
    }
    println!("point: {:?}", point);
}
​
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}
​

为什么需要ref和ref mut?

1.在模式匹配中,默认情况下,Rust 会移动或复制值的所有权。这在某些场景下会导致问题,尤其是当你需要保留原始值的所有权时。 ref 和 ref mut 允许你直接绑定为引用,而不是移动或复制值的所有权。

2.使用ref和ref mut可以简化模式匹配的语义,使得代码更加清晰和直观。这种设计使得开发者可以更容易地理解和维护代码。

6.模式匹配的优点

• 强大的解构能力:可以解构复杂的数据结构,提取出需要的值。

• 穷尽性检查:编译器会检查match是否覆盖了所有可能的模式,如果没有覆盖,会报错。

• 表达式返回值:match是一个表达式,可以返回值,这使得它在函数中非常有用。

• 代码简洁:相比if-elseswitchmatch更适合处理复杂的数据结构。

7.总结

模式匹配是 Rust 中一个非常强大的特性,它允许你以一种安全、高效且简洁的方式处理各种数据结构。通过matchif let,你可以实现复杂的逻辑,同时保持代码的可读性和可维护性。