Rust Option 模式匹配簡(jiǎn)介

Option

Option是rust非常好用的數(shù)據(jù)結(jié)構(gòu),用來解決 Null 空指針問題,是rust安全基石重要一環(huán)。其本質(zhì)是一個(gè) Enum 結(jié)構(gòu)。本文對(duì)option進(jìn)行模式匹配用于獲取 option 包裝的值的簡(jiǎn)單用法。

Option的聲明:

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

例子:


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

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

模式匹配

Option 是一個(gè)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 變體,則該方法會(huì) pannic。

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

opt 通過 unwarp 方法獲取變體 Some(x) 中的 x。若 opt 是 None 變體,unwarp 方法會(huì)pannic

uwranp的源碼:

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

從 unwarp的源碼可以看出,它本質(zhì)也是模式匹配的一種簡(jiǎn)寫。

所有權(quán)

Option的模式匹配和 unwarp 方法涉及所有權(quán)move語義。(x指沒有實(shí)現(xiàn) Copy trait的類型)

就像賦值,參數(shù)傳遞一樣。直接模式匹配會(huì)涉及所有權(quán)move

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

    println!("{:?}", opt);  // 所有權(quán)已move

上面的代碼會(huì)編譯錯(cuò)誤。錯(cuò)誤信息如下:

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 類型是沒有實(shí)現(xiàn) Copy trait,它存儲(chǔ)在堆上。創(chuàng)建 opt的時(shí)候,它的所有權(quán)move到 opt,通過模式匹配,所有權(quán)move到 x。x在match花括號(hào)的作用域后drop了。但是 opt 沒有所有權(quán),再次打印會(huì)報(bào)錯(cuò)。

unwrap 實(shí)現(xiàn)基于模式匹配,因此 unwarp 方法也會(huì)move 所有權(quán)。

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

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

編譯錯(cuò)誤信息:

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語義會(huì)轉(zhuǎn)移所有權(quán),使用借用 borrow語義就能保持所有權(quán)。

寫法一


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

    match &opt {
        Some(x) => println!("{}", x),     // 對(duì) &opt 進(jìn)行模式匹配,此時(shí)的 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,此時(shí) x 是 &String。 即將 opt所有的 String 的引用借給 x

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

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

很不幸,這樣還是會(huì)編譯錯(cuò)誤。&opt.unwrap(); 實(shí)際是 &(opt.unwrap)。所有權(quán)move之后再取引用。那么很容易想到下面的做法

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

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

這樣做依然會(huì)編譯失敗。即使是 &opt,unwarp的簽名是 self ,也就是 傳遞給 unwrap 的是 opt ,而不是 &opt。所有權(quán)還是轉(zhuǎn)移了。想要實(shí)現(xiàn) 所有權(quán)的借用,可以仿照 上面 match表達(dá)式的寫法。


    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

既然我們能想到封裝一個(gè) unwrap函數(shù),標(biāo)準(zhǔn)庫早也想到了。option的as_ref 源碼

    pub const fn as_ref(&self) -> Option<&T> {   // 將 opt 的引用&opt 作為參數(shù)
        match *self {                            // 對(duì) opt 進(jìn)行模式匹配
            Some(ref x) => Some(x),              // 通過 ref 獲取 x 引用,再封裝成 Option 返回
            None => None,
        }
    }

上面的 Some(ref x) => Some(x) 就是我們上面展示的引用的寫法二。過 as_ref 調(diào)用得到的是 Option<&String>。再調(diào)用 unwrap方法,就是對(duì)其進(jìn)行模式匹配,就是寫法二的方式:

    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);

總結(jié)

Option<T> 是 rust 類型安全重要思想的體現(xiàn)之一。它本質(zhì)是一個(gè) Enum 類型,有兩個(gè)變體,Some(x) 和 None。當(dāng)表示沒有值的時(shí)候,可以使用 None。其語義類似其他語言如 Python的None,Golang 的 nil, java 的null。但是又跟其他語言有本質(zhì)的不同。rust 的 None 是 Option 類型。而不是 其他任何類型。而其他語言的 None nil 可以是任何其他類型或引用類型。Null 可以是 字串,也可以是 指針。這就埋下了很多安全隱患。

Rust 的中表示可有可無的時(shí)候使用 Option。有值的時(shí)候需要使用 Some 變體。解出 Some(x) 中的 x 值方法是模式匹配。同時(shí)標(biāo)注庫也提供了便捷方法如 unwrap。

無論是使用 模式匹配 還是一些方法,對(duì)于所有權(quán)的move還是borrow嚴(yán)格遵循rust的所有權(quán)系統(tǒng)。通過上面介紹的幾個(gè)例子用以說明。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容