最常見的邏輯控制流比如if-else,switch,while等常用程式語言都支援,但恰巧rust語法中沒有switch,取而代之的是更加強大適用的match匹配,我們就來看看rust的match有何奇特之處。
一、介紹
先來看一個簡單的rust的match用法
enum Role{ Admin, User, Guest, Unkown } fn main(){ let role=Role::Admin; match role{ Role::Admin=>println!("you're an admin."), Role::User=>println!("you're a user."), Role::Guest=>println!("you're a guest."), _=>println!("deny to access.") } }
從這個例子可以看出,rust的match跟其它常用語言的switch功能相似。都是根據條件匹配分支。
比如C#實現:
enum { Admin, User, Guest, Unkown } void Main(){ var role=Role.Admin; switch(role) { case Role.Admin:{Console.WriteLine("you're an admin."); break;} case Role.User:{Console.WriteLine("you're an user."); break;} case Role.Guest:{Console.WriteLine("you're an guest."); break;} default:{Console.WriteLine("deny to access."); break;} } }
當然rust的match肯定不止於此,我們接著來看。
二、match配合變體enum解構
rust的變體enum可以包含不同資料型別,再加上match匹配可以輕鬆實現複雜的邏輯需求。
我們還是來看個例子,比如:
enum Operator{ Plus(i32,i32), Subtract(i32,i32), Multiply(i32,i32), Divide(i32,i32), Log10(i32) } fn main(){ let op=Operator::Plus(100,200); match op{ Operator::Plus(a,b)=>println!("{a}+{b}={}",a+b), Operator::Subtract(a,b)=>println!("{a}-{b}={}",a-b), Operator::Multiply(a,b)=>println!("{a}x{b}={}",a*b), Operator::Divide(a,b)=>println!("{a}/{b}={}",a/b), Operator::Log10(a)=>println!("log10({a})={}",a.ilog10()), } }
類似這樣的需求還是比較多的,比如處理滑鼠事件
enum MouseEvent{ MoveIn, MoveOut, Click(x,y), ... }
像這個例子在物件導向的程式語言中,肯定是使用型別繼承實現來到達目的,繼承並非不好,而是組合才是更佳思維方式,就好比社會各種組織都是靠共同作業,而非強行大一統,每個部分都有自身最高效的運作方式,強行一致這樣的組織也效率低下。
三、複雜match匹配臂
1、匹配區間模式
fn main() { let num=100; match num{ x @ 1 ..=3| x @ 6 ..=9=>println!("case 1.{x}"), 4|5=>println!("case 2.b"), x=>println!("case 3.{x}") } }
2、匹配萬用字元模式
fn main(){ let test=vec!["b", "m", "r","x","n","t","f","j"]; let s=(2,3,&test[0..3]); match s{ (_,3,[..,"j"])=>println!("case 1."), (2,_,["rust",end @ ..])=>println!("case 2.{:?}",end), (_,_,_)=>println!("case 3."), } }
當然還有一些其它的模式匹配就不一一說明了。
四、用match消除if-else
if-else是所有程式語言中最簡單直接的邏輯流控制方式,以至於被濫用了,在加上變數命名隨意,詞不達意使得程式碼難以理解(最近自嘲的」防禦式程式設計「例外),說實話三層if-else就足以讓人琢磨,我曾見到過十幾層的if-else,一個方法上千行,再後來曾幾何時程式設計流行消除if-else,以C++,Java,C#等流行物件導向語言大多使用設計模式來消除if-else,以至於設計模式被濫用了。
if-else並非是惡,既然提供了當用則用,無需顧慮太多。
在rust中對於單個Option<T>還是推薦用if let方式處理比較簡單,比如:
[Debug] struct User{ user_name:String, age:u8 } fn get_user(user_name:&str,pwd:&str)->Option<User>{ if user_name=="admin" && pwd=="123456"{ Some(User{user_name,age:20}) } else{ None } } fn main(){ let user=get_user("admin","123456"); if let Some(user)=user{ println!("login success {:?}",user); } }
我們應該謹防的是一個函數大段程式碼多層if-else巢狀,這個就是壞味道了。在其它常用程式語言中可能我們會用if-else寫出這樣的程式碼,虛擬碼比如:
if user.phone!=null{ if user.email!=null{ send_message(user.phone); send_email(user.email); log("優質客戶"); }else{ send_message(user.phone); log("普通客戶"); } }else{ if user.email!=null{ send_email(user.email); log("一般客戶"); }else{ log("待發展客戶"); } }
if-else是不是看得眼都花了,如果邏輯再複雜一些,這樣的if-else巢狀更多層,人都麻了,不上個設計模式都有點不好意思了。
我們來用rust的mach匹配看看如何消除使得程式碼更加清晰直觀。虛擬碼如下:
struct User{ phone:Option<String>, email:Option<String> } fn main(){ let user=User{phone:None,email:"[email protected]".to_owned()}; match(user.phone,user.email){ (Some(phone),Some(emial))=>{ send_message(user.phone); send_email(user.email); log("優質客戶"); }, (Some(phone),None)=>{ send_message(user.phone); log("普通客戶"); }, (None,Some(email))=>{ send_email(user.email); log("一般客戶"); }, (_,_)=>log("待發展客戶") } }
rust的match可以匹配多個目標,match使得層級單一了,整體邏輯是不是清晰多了。
好了,囉嗦了這麼多,感謝各位看官駐足在此停留。
文章在部落格園、微信公眾號等平臺釋出,轉載請註明來源(bmrxntfj)