Option
Option是rust非常好用的數(shù)據(jù)結(jié)構(gòu)循签,用來解決 Null 空指針問題熄攘,是rust安全基石重要一環(huán)嘁信。其本質(zhì)是一個 Enum 結(jié)構(gòu)。本文對option進(jìn)行模式匹配用于獲取 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的源碼可以看出映屋,它本質(zhì)也是模式匹配的一種簡寫苟鸯。
所有權(quán)
Option的模式匹配和 unwarp 方法涉及所有權(quán)move語義同蜻。(x指沒有實現(xiàn) Copy trait的類型)
就像賦值,參數(shù)傳遞一樣早处。直接模式匹配會涉及所有權(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
上面的代碼會編譯錯誤。錯誤信息如下:
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 類型是沒有實現(xiàn) Copy trait砌梆,它存儲在堆上默责。創(chuàng)建 opt的時候,它的所有權(quán)move到 opt咸包,通過模式匹配桃序,所有權(quán)move到 x。x在match花括號的作用域后drop了烂瘫。但是 opt 沒有所有權(quán)媒熊,再次打印會報錯。
unwrap 實現(xiàn)基于模式匹配坟比,因此 unwarp 方法也會move 所有權(quán)芦鳍。
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語義會轉(zhuǎn)移所有權(quán),使用借用 borrow語義就能保持所有權(quán)葛账。
寫法一
let opt = Some("hello".to_string());
match &opt {
Some(x) => println!("{}", x), // 對 &opt 進(jìn)行模式匹配,此時的 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)。所有權(quán)move之后再取引用宣谈。那么很容易想到下面的做法
let opt = Some("hello".to_string());
let s = (&opt).unwrap();
println!("{:?}", opt);
這樣做依然會編譯失敗愈犹。即使是 &opt,unwarp的簽名是 self 闻丑,也就是 傳遞給 unwrap 的是 opt 漩怎,而不是 &opt。所有權(quán)還是轉(zhuǎn)移了嗦嗡。想要實現(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
既然我們能想到封裝一個 unwrap函數(shù)侥祭,標(biāo)準(zhǔn)庫早也想到了叁执。option的as_ref 源碼
pub const fn as_ref(&self) -> Option<&T> { // 將 opt 的引用&opt 作為參數(shù)
match *self { // 對 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方法次哈,就是對其進(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ì)是一個 Enum 類型窑滞,有兩個變體,Some(x) 和 None恢筝。當(dāng)表示沒有值的時候哀卫,可以使用 None。其語義類似其他語言如 Python的None撬槽,Golang 的 nil此改, java 的null。但是又跟其他語言有本質(zhì)的不同侄柔。rust 的 None 是 Option 類型共啃。而不是 其他任何類型。而其他語言的 None nil 可以是任何其他類型或引用類型勋拟。Null 可以是 字串勋磕,也可以是 指針。這就埋下了很多安全隱患敢靡。
Rust 的中表示可有可無的時候使用 Option挂滓。有值的時候需要使用 Some 變體。解出 Some(x) 中的 x 值方法是模式匹配啸胧。同時標(biāo)注庫也提供了便捷方法如 unwrap赶站。
無論是使用 模式匹配 還是一些方法,對于所有權(quán)的move還是borrow嚴(yán)格遵循rust的所有權(quán)系統(tǒng)纺念。通過上面介紹的幾個例子用以說明贝椿。