Rust Option 和 Result 的理解

在這篇文章中,我將介紹 Option 和 Result 這兩種枚舉類型。

Option

Rust 版本的空類型是 Option 類型,它是一種枚舉類型,其中每個實例要麼是:

在這裏,value 可以是任何 T 類型的值。例如,Vec 有一個 pop() 方法,返回一個 Option,如果 vector 爲空,該方法將返回 None。

返回 Option 的 API 的好處之一是,要獲得其中的值,編譯器強制調用者必須檢查值是否爲 None。在 c++ 中,std::find() 返回一個迭代器,但是必須記得檢查它以確保它不是容器的 end()——如果忘記了這個檢查並試圖將值從容器中取出,將得到未定義的行爲。

缺點是,這會使代碼變得冗長,但是,Rust 有很多方法可以簡化!

使用 expect 和 unwrap

如果你確定一個 Option 裏面有一個真正的值,那麼 expect() 和 unwrap() 就是爲你準備的!它們返回內部的值,但如果變量實際上是 None,程序將退出。(這就是 panic,在某些情況下,panic 是可以恢復的,但爲了簡單起見,這裏我們將忽略這一點。)

惟一的區別是 expect() 允許你指定一個自定義消息,該消息在程序退出時打印到控制檯。

還有一個 unwrap_or(),它允許在值爲 None 時指定默認值,因此 Some(5).unwrap_or(7) 的值爲 5,None.unwrap_or(7) 的值爲 7。

如果你願意,你可以在調用 unwrap() 之前檢查 Option 是否有一個值,像這樣:

// t is an Option<T>
if t.is_some() {
  let real_value = t.unwrap();
}

但是,還有更簡潔的方法 (例如,使用 if let,我們稍後會介紹)。

使用 match

查看 Option 是否有值的最基本方法是使用匹配表達式進行模式匹配。這適用於任何枚舉類型,看起來像這樣:

// t is an Option<T>
match t {
  None => println!("No value here!"), // one match arm
  Some(x) => println!("Got value {}", x) // the other match arm
};

需要注意的一件事是,Rust 編譯器強制匹配表達式必須是窮盡的;也就是說,每個可能的值都必須被匹配。因此,下面的代碼無法編譯:

// t is an Option<T>
match t {
  Some(x) => println!("Got value {}", x)
};

我們得到一個錯誤:

error[E0004]: non-exhaustive patterns: `None` not covered

這實際上是非常有用的,以避免你認爲涵蓋了所有的情況。但是,如果你顯式地想忽略所有其他情況,你可以使用_ match 表達式:

// t is an Option<T>
match t {
  Some(x) => println!("Got value {}", x), // the other match arm
  _ => println!("OK not handling this case.");
};

使用 if let

只有當 Option 有一個實際有用的值時,可以使用 if let 這種簡潔的方法。例如,如果 t 有一個值,下面的代碼將打印 "Got ",如果 t 是 None 則不做任何操作:

// t is an Option<T>
if let Some(i) = t {
    println!("Got {}", i);
}

使用 map

還有很多方法可以對 Option 執行操作,而無需檢查它是否有值。例如,如果有實際值,可以使用 map() 轉換,否則將其保留爲 None。例如:

Some(10).map(|i| i + 1) == Some(11) 
None.map(|i| i + 1) == None

使用 into_iter

Vec<Option>,可以將其轉換爲 Option<Vec>,如果原始 Vector 中的任何項爲 None,則該值爲 None。

如果考慮從多個操作接收結果,並且希望在任何單個操作失敗時整個結果失敗,那麼這是有意義的。例如:

vec![Some(10), Some(20)].into_iter().collect() == Some([10, 20])
vec![Some(10), Some(20), None].into_iter().collect() == None

Result

Rust 的 Result<T, E> 類型是一種返回值或錯誤的枚舉類型。與 Option 類型類似,有兩種可能的變體:

使用 ok_or

由於 Option 和 Result 是如此相似,有一個簡單的方法在兩者之間轉換。

Option 的方法:

Some(10).ok_or("uh-oh") == Ok(10)
None.ok_or("uh-oh") == Err("uh-oh")

Result 的方法:

Ok(10).ok() == Some(10)
Err("uh-oh").ok() == None

Result 上還有一個 err() 方法,其作用與此相反:errors 被映射爲 Some,success 值被映射爲 None。

使用 expect, unwrap, match, and if let

就像使用 Option 一樣,Result 的 expect() 和 unwrap() 的工作方式與使用 Option 完全相同。而且,由於 Result 是一個枚舉類型,match 和 if let 也以相同的方式工作!

使用 "?" 操作符

很多時候,如果一個函數返回錯誤,你希望直接從調用函數中返回這個錯誤。所以,你的代碼看起來像下面這樣:

let inner_result = other_function();
if let Err(some_error) = inner_result {
    return inner_result;
}
let real_result = inner_result().unwrap();
// now real_result has the actual value we care about, we can continue on...

但是,一遍又一遍地返回錯誤是一種痛苦。相反,你可以這樣寫代碼:

let real_result = other_function()?;

更好的是,您可以將調用鏈接在一起,像這樣:

let real_result = this_might_fail()?.also_might_fail()?.this_one_might_fail_too()?;

另一種常見的技術是使用類似 map_err() 的方法將錯誤轉換爲符合外部函數返回的結果,然後使用? 操作符。

使用 into_iter

類似於 Option,如果你有 Vec<Result<T, E>>,你可以使用 into_iter() 和 collect() 將其轉換爲 Result<Vec, E>。例如:

vec![Ok(10), Ok(20)].into_iter().collect() == Ok([10, 20])
vec![Ok(10), Err("bad"), Ok(20), Err("also bad")].into_iter().collect() == Err("bad")

本文翻譯自:

https://blog.logrocket.com/understanding-rust-option-results-enums/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/CsRDn8Hi9MD__HYFQRKFUQ