Rust 基础知识23 - 模式匹配

模式匹配

  • 模式匹配通常由一下组件组成:

字面量。

解构的数组、枚举、结构体或者元组。

变量。

通配符

占位符。

知识汇总

模式的不可失败性和可失败性

  • 模式可以被分为不可失败(irrefutable) 和可失败(refutable)两种类型。
  • 例如 let x = 5; 中的 x 便是不可失败模式,因为它能够匹配表达式右侧的所有可能的返回值。
  • 例如 if let Some(x) = a_value 中的 Some(x) 便是可失败模式,如果 a_value 变量的值时 None 而不是 Some,那么表达式左侧的Some(x) 模式就会发生不匹配。
  • 了解这些概念十分重要,比如 let Some(x) = a_value 就会造成编译困扰,因为这个表达式是可失败的那么就要放到 if 中去。

if let 表达式

  • 举例:
fn main() {
    let favorite_color: Option<&str> = None;
    let is_tuesday = false;
    let age : Result<u8, _> = "34".parse();

    if let Some(color) = favorite_color {
        println!("Using your favorite color, {}, as the background", color);
    } else if is_tuesday {
        println!("Tuesday is green day!");
    } else if let Ok(age) = age {
        if age > 30 {
            println!("Using purple as the background color");
        } else {
            println!("Using orange as the background color");
        }
    }
}

while let 条件循环模式匹配

  • 例子:while let 会反复执行同一个模式匹配直到出现失败的情况。

fn main() {
    let mut stack = Vec::new();
    stack.push(1);
    stack.push(2);
    stack.push(3);
    while let Some(s) = stack.pop() {
        println!("Stack value : {}", s);
    }
}

for 循环进行元祖填充

  • 例子:注意不要忘记调用 enumerate 生成一个元祖迭代器

fn main() {
    let v = vec!["a", "b", "c"];
    for (index, value) in v.iter().enumerate() {
        println!("index : {}, value : {}",index, value );
    }
}

let 关键字本身就是一种模式匹配的方式

  • 例子:
// 如果模式元素中的数量与元祖的数量不符,那么就会导致匹配失败。
let (x, y, z) = (1, 2, 3);
// 如果确实有的不需要,可以通过 _ 或者 .. 进行省略
let (x, y, _) = (1, 2, 3);

函数参数也是模式匹配

  • 可以看到模式匹配无处不在,函数的参数也是模式匹配。
  • 例子:
// 实际上定义了一个元祖参数
fn print_coordinates(&(x, y): &(i32, i32)) {
    println!(" Current location : ({}, {})", x, y);
}

fn main() {
    let point = (55, 33);
    print_coordinates(&point);
}

匹配字面量

  • 例子:
fn main() {
    let x = 1 ;
    match x {
        1 => {println!("One"); },
        2 => {println!("Two"); },
        // 注意下面必须加上如果不加会提示没有穷尽所有解
        _ => {println!("Other"); },
    }
}

匹配命名变量

  • 例子:
fn main() {
    let x = Some(5);
    let y = 10 ;
    match x {
        Some(50) => println!("Get 50"),
        Some(y) => println!("Matched, y = {:?}", y ),
        _ => println!("Default case, x = {:?}", x),
    }

    println!("at the end : x = {:?}, y = {:?}", x, y);
}

多重匹配

  • 例子:
fn main() {
    let x = 1 ;
    match x {
        1 | 2 => println!("one or two"),
        3 => println!("three"),
        _ => println!("anything."),
    }
}

使用 ... 来匹配区间

  • 例子:
fn main() {
    let x = 7;
    match x {
        1 ... 9 => println!("one through nine"),
        3 => println!("three"),
        _ => println!("anything."),
    }
}

使用 ..= 来匹配字符区间

  • 例子:
fn main() {
    let x = 'c';
    match x {
        'a' ..= 'd' => println!("a | b | c | d"),
        'f' ..= 'i' => println!("f | g | h | i"),
        _ => println!("anything."),
    }
}

使用解构来分解值(分解结构)

  • 例子:
fn main() {

    struct Point {
        x: i32,
        y: i32,
    }

    let point = Point {
        x: 32,
        y: 66
    };

    // 解构
    let Point {x : a, y : b } = point;
    println!("x:{} , y:{}", a,  b);
}

稍稍复杂一点的match 匹配

  • 看了这个例子突然感觉模式匹配很有用,会的:
fn main() {
    struct Point {
        x: i32,
        y: i32,
    }
    let point = Point {
        x: 0,
        y: 7
    };
    // 进行更复杂一点的模式匹配
    match point {
        Point{x, y:0} => println!("On the x axis at {}", x),
        Point{x:0, y} => println!("On the y axis at {}", y),
        // 注意如下最不能缺少的就是这个,没有这个兜底,那么匹配就不能穷尽。
        Point{x,y} => println!("On neither axis: ({}, {})", x,y),
    }
}

解构嵌套的结构体和枚举:

  • 例子:
enum Color {
    Rgb (i32,i32,i32),
    Hvs (i32,i32,i32),
}

enum Message {
    Quit,
    Write(String),
    ChangeColor(Color),
}

fn main() {
    let msg = Message::ChangeColor(Color::Rgb(6,6,6));
    match msg {
        Message::ChangeColor(Color::Rgb(r,g,b)) => {
            println!("r={},g={},b={}", r,g,b);
        },
        Message::ChangeColor(Color::Hvs(h,v,s)) => {
            println!("h={},v={},s={}", h,v,s);
        },
        _ => {}
    }
}

使用 _ (下划线) 忽略变量值时需要特别注意的一个问题

  • 例子,错误的

fn main() {
    let s = Some(String::from("Hello."));
    if let(_s) = s {
        println!("Hello , into there.")
    }
    // 这段代码编译不过去,虽然 _s 但是所有权仍然会转移
    println!("s value is : {:?} ", s);
}

  • 例子,正确的,_ 单纯的下滑线就不会造成所有权转移,稍稍修改一下。
fn main() {
    let s = Some(String::from("Hello."));
    // 修改一下这里,去掉 _ 后面的任何值,所有权就不会转移。
    if let(_) = s {
        println!("Hello , into there.")
    }
    println!("s value is : {:?} ", s);
}

使用 .. (双点) 忽略匹配值

  • 例子:

struct Point {
    x: i32, y:i32 , z:i32,
}

fn main() {
    let p = Point {x:3,y:4,z:5};
    // 如果只是想匹配x 可以使用双点语法。
    let Point{x, ..} = p;
    assert_eq!(3, x);
}

为了匹配更复杂的模式可以使用match guard

  • 就是在模式匹配中新增一个 if 条件,举例:
fn main() {
    let num = Some(4);
    match num {
        Some(x) if x < 5 => { println!("Num {} less than five. ", x) },
        Some(x) => { println!("Num is {} . ", x) },
        None => {},
    }
}


  • 注意一个情况:
match x {
    4 | 5 | 6 if y => { ... } 
}

实际上等于

match x {
    (4 | 5 | 6) if y => { ... }
    // 而不是 
    4 | 5 | (6 if y) => { ... }
}

模式匹配中的绑定

  • 有些范围匹配即便成功了,我们也无法捕获到底是什么值匹配成功的。
  • 这时候就需要在匹配中绑定变量需要通过 @ 运算符。
fn main() {
    enum Message {
        Hello {id: i32}
    }

    let msg = Message::Hello {id: 5};

    match msg {
        // 注意这里,如果不用 id_variable@ 进行变量捕获,下面的println 就无法直到具体的值是什么。
        Message::Hello {id: id_variable@ 3..=7} => {
            println!("Found an id in range : {} ", id_variable);
        },
        Message::Hello {id:10...12} => {
            println!("Found id between 10 to 12");
        },
        Message::Hello {id} => {
            println!("Found some other id : {}", id);
        },
    }
}

结束

  • 感谢阅读,See you at work.
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357

推荐阅读更多精彩内容

  • if case 在学习 if case之前,我们先想想,那些地方使用到了case 这个关键字,毫无疑问,绝大多数使...
    枫叶1234阅读 2,326评论 0 3
  • 通用编程概念 变量与可变性 变量默认不可变,如需要改变,可在变量名前加 mut 使其可变。例如:let mut a...
    soojade阅读 12,571评论 2 30
  • 01-前端开发和前端开发工具 一、前端开发 PRD(产品原型-产品经理) - PSD(视觉设计-UI工程师) - ...
    刊ing阅读 247评论 0 0
  • Swift里的switch比OC里面强大很多,switch的主要特性就是模式匹配。下面先举个非常简单的例子。 看完...
    ChoiKarl阅读 2,130评论 3 11
  • Scala强大的模式匹配机制,可以应用在switch语句、类型检查以及“析构”等场合。样本类对模式匹配进行了优化。...
    彤庆阅读 499评论 0 1