Rust Option 模式匹配简介

Option

Option是rust非常好用的数据结构,用来解决 Null 空指针问题,是rust安全基石重要一环。其本质是一个 Enum 结构。本文对option进行模式匹配用于获取 option 包装的值的简单用法。

Option的声明:

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

例子:


    let opt = Some("hello".to_string());

    println!("{:?}", opt);  // 输出: Some("hello")

模式匹配

Option 是一个Enum,通过模式匹配获取其变体

Some(T) 变体

    let opt = Some("hello".to_string());
    match opt {
        Some(x) => println!("Some: x={}", x), // Some: x=hello
        None => println!("None")
    }

None 变体

    let opt:Option<String> = None;
    match opt {
        Some(x) => println!("Some: x={}", x), 
        None => println!("None") // None
    }

变量 opt 可以是 None 变体。上面的 opt 需要指定类型,不然这段代码编译器无法推断 x 的类型。

unwarp方法

Option 有很多有用的方法。unwarp 方法用于获取 Some(x) 中 的 x 值。如果 Option是 None 变体,则该方法会 pannic。

    let opt = Some("hello".to_string());
    let s = opt.unwrap();
    println!("{}", s); // s

opt 通过 unwarp 方法获取变体 Some(x) 中的 x。若 opt 是 None 变体,unwarp 方法会pannic

uwranp的源码:

    pub const fn unwrap(self) -> T {
        match self {
            Some(val) => val,
            None => panic!("called `Option::unwrap()` on a `None` value"),
        }
    }

从 unwarp的源码可以看出,它本质也是模式匹配的一种简写。

所有权

Option的模式匹配和 unwarp 方法涉及所有权move语义。(x指没有实现 Copy trait的类型)

就像赋值,参数传递一样。直接模式匹配会涉及所有权move

    let opt = Some("hello".to_string());
    match opt {
        Some(x) => println!("Some: x={}", x),  // 模式匹配,所有权move
        None => println!("None") 
    }

    println!("{:?}", opt);  // 所有权已move

上面的代码会编译错误。错误信息如下:

error[E0382]: borrow of partially moved value: `opt`
  --> src/main.rs:54:22
   |
50 |         Some(x) => println!("Some: x={}", x),
   |              - value partially moved here
...
54 |     println!("{:?}", opt);
   |                      ^^^ value borrowed here after partial move
   |
   = note: partial move occurs because value has type `String`, which does not implement the `Copy` trait
help: borrow this field in the pattern to avoid moving `opt.0`

String 类型是没有实现 Copy trait,它存储在堆上。创建 opt的时候,它的所有权move到 opt,通过模式匹配,所有权move到 x。x在match花括号的作用域后drop了。但是 opt 没有所有权,再次打印会报错。

unwrap 实现基于模式匹配,因此 unwarp 方法也会move 所有权。

    let opt = Some("hello".to_string());
    let s = opt.unwrap();

    println!("{:?}", opt);

编译错误信息:

48  |     let opt = Some("hello".to_string());
    |         --- move occurs because `opt` has type `Option<String>`, which does not implement the `Copy` trait
49  |     let s = opt.unwrap();
    |                 -------- `opt` moved due to this method call
50  | 
51  |     println!("{:?}", opt);
    |                      ^^^ value borrowed here after move
    |

引用

move语义会转移所有权,使用借用 borrow语义就能保持所有权。

写法一


    let opt = Some("hello".to_string());

    match &opt {
        Some(x) => println!("{}", x),     // 对 &opt 进行模式匹配,此时的 x 是 &String 类型 
        None => println!("None"),
    }
    println!("{:?}", opt);  // 输出 Some("hello")

写法二

    let opt = Some("hello".to_string());

    match opt {
        Some(ref x) => println!("{}", x),
        None => println!("None"),
    }
    println!("{:?}", opt);

opt 依然是正常的形式,不是其引用,在 Some 中使用 ref 修饰 x,此时 x 是 &String。 即将 opt所有的 String 的引用借给 x

    let opt = Some("hello".to_string());

    let s = &opt.unwrap();
    println!("{:?}", opt);

很不幸,这样还是会编译错误。&opt.unwrap(); 实际是 &(opt.unwrap)。所有权move之后再取引用。那么很容易想到下面的做法

    let opt = Some("hello".to_string());

    let s = (&opt).unwrap();
    println!("{:?}", opt);

这样做依然会编译失败。即使是 &opt,unwarp的签名是 self ,也就是 传递给 unwrap 的是 opt ,而不是 &opt。所有权还是转移了。想要实现 所有权的借用,可以仿照 上面 match表达式的写法。


    let opt = Some("hello".to_string());

    let s = unwrap(&opt); 
    println!("{:?}", s);    // hello
    println!("{:?}", opt);  // Some("hello")


 fn unwrap(opt: &Option<String>) -> &String {
    match opt {
        Some(x) => x,
        None => panic!("called `Option::unwrap()` on a `None` value"),
    }
}
   

as_ref

既然我们能想到封装一个 unwrap函数,标准库早也想到了。option的as_ref 源码

    pub const fn as_ref(&self) -> Option<&T> {   // 将 opt 的引用&opt 作为参数
        match *self {                            // 对 opt 进行模式匹配
            Some(ref x) => Some(x),              // 通过 ref 获取 x 引用,再封装成 Option 返回
            None => None,
        }
    }

上面的 Some(ref x) => Some(x) 就是我们上面展示的引用的写法二。过 as_ref 调用得到的是 Option<&String>。再调用 unwrap方法,就是对其进行模式匹配,就是写法二的方式:

    let opt = Some("hello".to_string());

    let opt1 = opt.as_ref();   // as_ref 获取 opt x的引用

    match opt1 {                        // 模式匹配
        Some(x) => println!("{}", x),   // x 是 &String 
        None => println!("None"),
    }
    println!("{:?}", opt);
    println!("{:?}", opt1);

上面的过程可以连起来写成一行

    let opt = Some("hello".to_string());

    let s = opt.as_ref().unwrap();
    println!("{:?}", s);
    println!("{:?}", opt);

总结

Option<T> 是 rust 类型安全重要思想的体现之一。它本质是一个 Enum 类型,有两个变体,Some(x) 和 None。当表示没有值的时候,可以使用 None。其语义类似其他语言如 Python的None,Golang 的 nil, java 的null。但是又跟其他语言有本质的不同。rust 的 None 是 Option 类型。而不是 其他任何类型。而其他语言的 None nil 可以是任何其他类型或引用类型。Null 可以是 字串,也可以是 指针。这就埋下了很多安全隐患。

Rust 的中表示可有可无的时候使用 Option。有值的时候需要使用 Some 变体。解出 Some(x) 中的 x 值方法是模式匹配。同时标注库也提供了便捷方法如 unwrap。

无论是使用 模式匹配 还是一些方法,对于所有权的move还是borrow严格遵循rust的所有权系统。通过上面介绍的几个例子用以说明。

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

推荐阅读更多精彩内容