👈 4 流程控制

matchif let

match 匹配

match 的通用形式:

match tar {
	模式 1 => 表达式 1, // 单条表达式
	模式 2 => { // 表达式块
		语句 1;
		语句 2;
		表达式 2
	},
	_ => 表达式 3 // 用 _ 指代默认情况
}
enum Direction {
    East,
    West,
    North,
    South,
}
 
fn main() {
    let dir = Direction::South;
    match dir {
        Direction::East => println!("East"),
        Direction::North | Direction::South => { // 匹配多个模式
            println!("South / North");
        },
        _ => println!("West"),
    };
}

match 匹配枚举类型时:

  • 所有分支需要完全覆盖枚举变量的所有成员类型。
  • 用通配符 _ 表示未列出的其他模式。
  • 在同一分支中可以用 | 匹配多个模式。
  • 每个分支必须返回一个表达式结果,且类型必须相同。

match 本身也是表达式,可用于赋值语句:

enum IpAddr {
   Ipv4,
   Ipv6
}
 
fn main() {
    let ip = IpAddr::Ipv6;
    let my_ip = match ip {
        IpAddr::Ipv4 => "127.0.0.1",
        IpAddr::Ipv6 => "::1",
    };
    println!("{my_ip}");
}

也可用于函数返回值:

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}
 
fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        },
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

对于枚举 Coin,25 美分硬币上有各种州的图案,可将枚举 UsState 绑定到 Coin::Quarter 成员,称为模式绑定:

enum UsState {
    Alabama,
    Alaska,
    // ...
}
 
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState), // quarter 特有不同的州图案
}
 
fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        },
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => { // 用 state 匹配 UsState 实例
            println!("State quarter from {:?}!", state);
            25
        },
    }
}

更复杂的例子:

enum Action {
    Note(String),
    MoveTo(i32, i32),
    SetColor(u16, u16, u16),
}
 
fn main() {
    let actions = [
        Action::Note("Match!".to_string()),
        Action::MoveTo(1, 2),
        Action::SetColor(255, 255, 0),
    ];
    for action in actions {
        match action {
            Action::Note(s) => {
                println!("{s}");
            }
            Action::MoveTo(x, y) => {
                println!("point moved to ({x}, {y})");
            }
            Action::SetColor(r, g, _) => {
                println!("set color to '(r:{r}, g:{g}, b:0)', 'b' has been ignored");
            }
        }
    }
}

有些模式不重要,但又必须覆盖,可以使用通配模式或 _ 占位符:

// 通配模式: 匹配任何值并使用
let difficulty = 3;
match difficulty {
	1 => println!("Easy~"),
	2 => println!("Hard!"),
	other => println!("{other} is not valid"),
};
 
// _ 占位符: 匹配任何值并丢弃
let difficulty = 3;
match difficulty {
	1 => println!("Easy~"),
	2 => println!("Hard!"),
	_ => println!("not valid"),
};

if let 匹配

只想匹配一种模式时,使用 match 将比较复杂:

let v = Some(3u8);
match v {
	Some(3) => println!("v = 3"),
	_ => (),
};

而使用 if let 则简洁一些:

let v = Some(3u8);
if let Some(3) = v {
	println!("v = 3");
}

if letmatch 在只希望匹配一种模式下的语法糖:

// match
let config_max = Some(3u8);
match config_max {
	Some(max) => println!("The maximum is configured to be {max}"),
	_ => (),
}
 
// 等价的 if let
let config_max = Some(3u8);
if let Some(max) = config_max {
	println!("The maximum is configured to be {max}");
}

if let 后可以跟 else

for coin in coins.iter() {
	if let Coin::Quarter(state) = coin {
		println!("State quarter from {:?}!", state);
	} else {
		println!("Not a quarter");
	}
}

在简单的例子中,用 if 语句更合适:

let v = Some(3u8);
if v == Some(3) {
	println!("v = 3");
}

不过解构更复杂的情况才是 if let 的用武之地:

enum Result {
    Success(i32),
    Failure(String),
}
 
fn main() {
    let res = Result::Success(123);
    if let Result::Success(val) = res {
        println!("Operation succeeded with value: {val}");
    } else {
        println!("Operation failed.");
    }
}

matchif let 的选择:如果只想要匹配一个条件而忽略其余条件,使用 if let,否则使用 match

matches!

matches! 宏将一个表达式与模式进行匹配,返回一个 bool 表示匹配结果。

enum MyEnum {
    Foo,
    Bar,
}
 
fn main() {
    let v = vec![MyEnum::Foo, MyEnum::Bar, MyEnum::Foo];
}

现需要对 v 进行过滤,只保留类型为 MyEnum::Foo 的元素:

// not ok
v.iter().filter(|x| x == MyEnum::Foo);

这是不正确的,应该使用 matches! 宏:

// ok
v.iter().filter(|x| matches!(x, MyEnum::Foo));

更多示例:

let foo = 'f';
assert!(matches!(foo, 'A'..='Z' | 'a'..='z')); // success
 
let bar = Some(4);
assert!(matches!(bar, Some(x) if x > 2)); // success

解构 Option

Option 的定义:

enum Option<T> {
	Some(T),
	None,
}

一个 Option 实例要么有值 Some(T),要么为空 None

使用 Option<T> 的意义是从 Some 中取出 T 值,或者处理值为空的情况。

fn main() {
	let five = Some(5);
	let six = plus_one(five); // Some(6)
	let none = plus_one(None); // None
}
 
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
	    Some(i) => Some(i + 1),
        None => None,
    }
}

plus_one 接收一个 Option<i32>,并返回一个 Option<i32>

  • 如果传入一个 Some(i32),则通过 match 将其中的值绑定到 i,再返回 Some 包裹的 i+1
  • 如果传入一个 None,则直接返回一个 None

模式适用场景

  • match 分支
  • if let 分支
  • while let 条件循环
  • for 循环
  • let 语句
  • 函数参数

while let 条件循环

类似于 if let,不过只要模式一直匹配就保持循环。

let mut stack = Vec::new();
 
stack.push(1);
stack.push(2);
stack.push(3);
 
while let Some(top) = stack.pop() { // 弹完即止
    println!("{top}");
}

函数参数

参数即为模式,还可以在参数中匹配元组:

fn print_point(&(x, y): &(i32, i32)) {
	println!("({x}, {y})");
}

全模式列表

参考:全模式列表

👉 6 方法