Rust 元組匹配技巧

我喜歡 Rust 的一個原因是它無處不在的模式匹配。

在我目前的項目 (一個多人遊戲服務器) 中,我所面臨的一個常見情況是:

1,運行多個函數,返回類型都是 Result<T,String>

2,如果他們都成功了,那就繼續執行

3,如果其中任何一個失敗,則使用一個錯誤執行一個錯誤場景。

例如,如果一個玩家施放了火球,我可能需要計算火球的大小、傷害和速度——三個獨立的計算。如果其中任何一個失敗了,就會出現問題,火球就不能施放了。

這也是編程中的一個普遍問題:在同一時間有多個表達式分支。

我最初的解決方案是做一堆嵌套檢查:

 1if let Ok(response1) = func1() {
 2  if let Ok(response2) = func2() {
 3    if let Ok(response3) = func3() {
 4      handleResponse(response1, response2, response3)
 5   } else if let Err(e) {
 6      handleError(e)
 7    }
 8  } else if let Err(e) {
 9    handleError(e)
10  }
11} else if let Err(e) {
12  handleError(e)
13}

這是…… 混亂!至少可以這麼說。很難使其併發,儘管在某些情況下這可能是首選 (在 func1 完成之前不能執行 func2)。但這裏的業務邏輯本質上是,“計算 func1,檢查是否有錯誤,如果有,就處理它。計算 func2,檢查是否有錯誤…”,這很難讀懂。

所以我試着用元組來簡化它。我學到了一個很酷的技巧:

1match (func1(), func2(), func3()) {
2    (Ok(r1), Ok(r2), Ok(r3)) => handleResponse(r1, r2, r3),
3    (Err(e), _, _) | 
4    (_, Err(e), _) |
5    (_, _, Err(e) => handleError(e)
6}

注意,在三個不同的匹配情況下,e 來自三個不同的變量。但只要每種情況的 Err 類型相同,Rust 就允許我們在單個表達式中使用不同的變量。

如果我們不關心錯誤響應,當然我們可以進一步簡化:

1match (func1(), func2(), func3()) {
2  (Ok(r1), Ok(r2), Ok(r3)) => handleResponse(r1, r2, r3),
3  _ => handleError()
4}

這是一個很酷的技巧,但對我來說,這是一個範式的突破。對於非相關函數,我們可以在一個元組中聚合它們的結果,然後進行模式匹配。它允許我從業務邏輯而不是代碼的角度進行思考。

使用?使用內聯閉包的語法,我們可以進一步簡化:

1match (|| Ok(func1()?, func2()?, func3()?))() {
2    Ok((r1, r2, r3)) => handleResponse(r1, r2, r3),     
3    Err(e) => handleError(e),
4}

雖然閉包語法有點不清楚,但這種風格的一個優點是,如果第一個函數返回一個錯誤,第二個和第三個函數就不會被執行。我們也可以使用函數代替閉包來增加清晰度:

 1fn calc() -> Result<(R1, R2, R3), E> { 
 2    let r1 = func1()?; 
 3    let r2 = func2()?; 
 4    let r3 = func3()?; 
 5    Ok((r1, r2, r3))
 6}
 7
 8match calc() { 
 9    Ok(r) => handle_response(r), 
10    Err(e) => handle_error(e), 
11}

還有一個方法是使用 and_then,但這是最不清晰的方法:

1let result = func1()
2    .and_then(|r1| func2().map(|r2| (r1, r2)))
3    .and_then(|(r1, r2)| func3().map(|r3| (r1, r2, r3)));
4
5match result { 
6    Ok(r) => handle_response(r), 
7    Err(e) => handle_error(e), 
8}

本文翻譯自:

https://nathanael-morris-bennett.medium.com/rust-tuple-pattern-matching-trick-c0f6bcdb4460

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