首頁 > 軟體

Rust指南列舉類與模式匹配詳解

2022-09-12 18:01:36

前言

書接上文,本篇部落格分享的是Rust 列舉類與模式匹配 的知識。作為安全性強的語言,Rust 的列舉類並不像其他程式語言中的概念那樣簡單,但依然可以十分簡單的使用。

1、Rust基本列舉類語法

列舉允許我們列舉所有可能的值來定義一個型別,列舉中的值也叫變體

1.1、定義列舉

我們舉一個例子:

IP地址:IPV4、IPV6

enum IpAddrKind{
	V4,
	V6
}

接收列舉值

let four=IpAddrKind::V4;
let six=IpAddrKind::V6;

列舉的變體都位於識別符號的名稱空間下,使用::進行分割

1.2、將資料附加到列舉的變體中

形式如下:

enum IpAddr{
	V4(String),
	V6(String)
}

優點:

  • 不需要額外使用struct來確定型別
  • 每個變體可以擁有不同的型別以及關聯的資料量

例如:

#[derive(Debug)]
enum IpAddrKind {
    V4(u8,u8,u8,u8),
    V6(String)
}

fn main() {
    let home=IpAddrKind::V4(127, 0, 0, 1);
    let loopback=IpAddrKind::V6(String::from("這是IPV6"));
    println!("{:?}n{:?}",home,loopback);
}

執行效果:

  • #[derive(Debug)]作為Rust提供的偵錯庫是可以直接輸出結構體和列舉型別的
  • 但是注意預留位置只能使用{:?}
  • 標準庫中的IpAddr
struct IpV4Addr{
    //--snip--
}
struct IpV6Addr{
    //--snip--
}

enum IpAddr {
    V4(IpV4Addr),
    V6(IpV6Addr)
}

1.3、變體的多種巢狀方式

enum Message {
    Quit,
    Move {x:i32,y:u32},
    Write(String),
    ChangeColor(i32,i32,i32)
}

fn main() {
    let q=Message::Quit;
    let m=Message::Move { x: 6, y: 12 };
    let w=Message::Write(String::from("hello_world"));
    let c=Message::ChangeColor(255, 255, 0);
}

在這段程式碼中列舉類變體一共有四種資料型別:

  • 不帶關聯資料Quit
  • 匿名結構體Move
  • 字串型別Write
  • 匿名元組結構體ChangeColor

1.4、定義列舉方法

和結構體方法類似,使用impl關鍵字:

impl Message{
	fn call(&self){}
}

這裡就不具體實現了,此時列舉的所有變體都可以呼叫call方法,例如q.call();

2、Option列舉

2.1、引入Option列舉解決控制問題

  • OptionRust 標準庫中的列舉類,這個類用於填補 Rust 不支援 null 參照的空白。
  • 許多語言支援 null 的存在(C/C++、Java),這樣很方便,但也製造了極大的問題,null 的發明者也承認這一點,“一個方便的想法造成累計 10 億美元的損失”
  • null 經常在開發者把一切都當作不是 null 的時候給予程式致命一擊:畢竟只要出現一個這樣的錯誤,程式的執行就要徹底終止
  • 為了解決這個問題,很多語言預設不允許 null,但在語言層面支援 null 的出現(常在型別前面用 ? 符號修飾)。
  • Java 預設支援 null,但可以通過 @NotNull 註解限制出現 null,這是一種應付的辦法。

Rust 在語言層面徹底不允許空值 null 的存在,但無奈null 可以高效地解決少量的問題,所以 Rust 引入了 Option 列舉類:

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

2.2、列舉類的具體使用

列舉類包含在預匯入模組中(Prelude),可直接使用:

let some_number=Some(5);
let some_string=Some("a string")

let absent:Option<&str>=None;

注意:

  • 編譯器無法推斷None是什麼型別,所以一定要顯示宣告
  • 由於absent屬於None的變體,因此是無效資料,也就是null

3、match控制流運運算元

  • 列舉的目的是對某一類事物的分類,分類的目的是為了對不同的情況進行描述。
  • 基於這個原理,往往列舉類最終都會被分支結構處理(許多語言中的 switch )。
  • switch 語法很經典,但在 Rust 中並不支援,很多語言摒棄 switch 的原因都是因為 switch 容易存在因忘記新增 break 而產生的串接執行問題,Java 和 C# 這類語言通過安全檢查杜絕這種情況出現。

Rust 通過 match 語句來實現分支結構。先認識一下如何用 match 處理列舉類:

fn main() {
    enum Book {
        Papery {index: u32},
        Electronic {url: String},
    }
   
    let book = Book::Papery{index: 1001};
    let ebook = Book::Electronic{url: String::from("url...")};
   
    match book {
        Book::Papery { index } => {
            println!("Papery book {}", index);
        },
        Book::Electronic { url } => {
            println!("E-book {}", url);
        }
    }
}
//執行結果:Papery book 1001

這是由於book屬於Papery的變體,因此會執行第一個列印語句

match 塊也可以當作函數表示式來對待,它也是可以有返回值的:

match 列舉類範例 {
    分類1 => 返回值表示式,
    分類2 => 返回值表示式,
    ...
}

但是要謹記:所有返回值表示式的型別必須一樣!

如果把列舉類附加屬性定義成元組,在 match 塊中需要臨時指定一個名字:

enum Book {
    Papery(u32),
    Electronic {url: String},
}
let book = Book::Papery(1001);

match book {
    Book::Papery(i) => {
        println!("{}", i);
    },
    Book::Electronic { url } => {
        println!("{}", url);
    }
}

變體Papery指定了i變數,Electronic指定了url

match 除了能夠對列舉類進行分支選擇以外,還可以對整數、浮點數、字元和字串切片參照(&str)型別的資料進行分支選擇。其中,浮點數型別被分支選擇雖然合法,但不推薦這樣使用,因為精度問題可能會導致分支錯誤。

對非列舉類進行分支選擇時必須注意處理例外情況,即使在例外情況下沒有任何要做的事。例外情況用下劃線 _ 表示:

fn main() {
    let t = "abc";
    match t {
        "abc" => println!("Yes"),
        _ => {},
    }
}

4、if let 語法

通過一個簡單的流程控制程式碼理解此部分知識:

let i = 0;
match i {
    0 => println!("zero"),
    _ => {},
}
//主函數中執行結果:zero

這段程式的目的是判斷 i 是否是數位 0,如果是就列印 zero。

那麼現在用 if let 語法縮短這段程式碼:

let i = 0;
if let 0 = i {
    println!("zero");
}

if let 語法格式如下:

if let 匹配值 = 源變數 {
    語句塊
}
  • 可以在之後新增一個 else 塊來處理例外情況。

if let 語法可以認為是隻區分兩種情況的 match 語句的"語法糖"

在列舉類中的使用:

fn main() {
    enum Book {
        Papery(u32),
        Electronic(String)
    }
    let book = Book::Electronic(String::from("url"));
    if let Book::Papery(index) = book {
        println!("Papery {}", index);
    } else {
        println!("Not papery book");
    }
}
//執行結果:Not papery book

Rust 列舉類和模式匹配的知識就分享到這裡了,期待你的鼓勵,這將是我創作的不竭動力!

到此這篇關於Rust指南列舉類與模式匹配精講的文章就介紹到這了,更多相關Rust列舉類與模式匹配內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com