Rust 語言學習之旅(2)

banq發表於2022-09-29

列舉
列舉允許你使用 enum 關鍵字建立一個新型別,該型別的值可以包含幾個帶標記的元素。
match 有助於確保對所有可能的列舉值進行徹底的處理,使其成為確保高質量程式碼的強大工具。

#![allow(dead_code)] // this line prevents compiler warnings

enum Species {
    Crab,
    Octopus,
    Fish,
    Clam
}

struct SeaCreature {
    species: Species,
    name: String,
    arms: i32,
    legs: i32,
    weapon: String,
}

fn main() {
    let ferris = SeaCreature {
        species: Species::Crab,
        name: String::from("Ferris"),
        arms: 2,
        legs: 4,
        weapon: String::from("claw"),
    };

    match ferris.species {
        Species::Crab => println!("{} is a crab",ferris.name),
        Species::Octopus => println!("{} is a octopus",ferris.name),
        Species::Fish => println!("{} is a fish",ferris.name),
        Species::Clam => println!("{} is a clam",ferris.name),
    }
}


帶資料的列舉
enum 的元素可以有一個或多個資料型別,從而使其表現得像 C 語言中的聯合。
當使用 match 對一個 enum 進行模式匹配時,可以將變數名稱繫結到每個資料值。
enum 的記憶體細節:

  • 列舉資料的記憶體大小等於它最大元素的大小。此舉是為了讓所有可能的值都能存入相同的記憶體空間。
  • 除了元素資料型別(如果有)之外,每個元素還有一個數字值,用於表示它是哪個標籤。

其他細節:
  • Rust 的 enum 也被稱為標籤聯合 (tagged-union)
  • 把型別組合成一種新的型別,這就是人們所說的 Rust 具有 代數型別 的含義。


#![allow(dead_code)] // this line prevents compiler warnings

enum Species { Crab, Octopus, Fish, Clam }
enum PoisonType { Acidic, Painful, Lethal }
enum Size { Big, Small }
enum Weapon {
    Claw(i32, Size),
    Poison(PoisonType),
    None
}

struct SeaCreature {
    species: Species,
    name: String,
    arms: i32,
    legs: i32,
    weapon: Weapon,
}

fn main() {
    // SeaCreature's data is on stack
    let ferris = SeaCreature {
        // String struct is also on stack,
        // but holds a reference to data on heap
        species: Species::Crab,
        name: String::from("Ferris"),
        arms: 2,
        legs: 4,
        weapon: Weapon::Claw(2, Size::Small),
    };

    match ferris.species {
        Species::Crab => {
            match ferris.weapon {
                Weapon::Claw(num_claws,size) => {
                    let size_description = match size {
                        Size::Big => "big",
                        Size::Small => "small"
                    };
                    println!("ferris is a crab with {} {} claws", num_claws, size_description)
                },
                _ => println!("ferris is a crab with some other weapon")
            }
        },
        _ => println!("ferris is some other animal"),
    }
}



泛型
泛型在 Rust 中非常重要。它們用於表示可空值(即可能還沒有值的變數)、錯誤處理、集合等等! 在本章中,我們將學習你可能將會經常使用的基本泛型知識。

泛型允許我們不完全定義一個 struct 或 enum,使編譯器能夠根據我們的程式碼使用情況,在編譯時建立一個完全定義的版本。
Rust 通常可以透過檢視我們的例項化來推斷出最終的型別,但是如果需要幫助,你可以使用 ::<T> 運算子來顯式地進行操作, 該運算子也被稱為 turbofish (它是我的好朋友!)。

// 一個部分定義的結構體型別
struct BagOfHolding<T> {
    item: T,
}

fn main() {
    // 注意:透過使用泛型,我們建立了編譯時才建立的型別,使程式碼更大
    // Turbofish 使之顯式化
    let i32_bag = BagOfHolding::<i32> { item: 42 };
    let bool_bag = BagOfHolding::<bool> { item: true };
    
    // Rust 也可以推斷出泛型的型別!
    let float_bag = BagOfHolding { item: 3.14 };

    // 注意:在現實生活中,不要把一袋東西放在另一袋東西里:)
    let bag_in_bag = BagOfHolding {
        item: BagOfHolding { item: "嘭!" },
    };

    println!(
        "{} {} {} {}",
        i32_bag.item, bool_bag.item, float_bag.item, bag_in_bag.item.item
    );
}


空null
在其他語言中,關鍵字 null 用於表示沒有值。它給程式語言帶來了困難,因為它使我們的程式在與變數欄位互動時可能失敗。
Rust 沒有 null,但這並不代表我們不知道表示空的重要性!我們可以使用一個我們已經瞭解過的工具來簡單地表示這一點。
因為缺少 null 值,這種為一個或多個替代值提供 None 替代表示的模式非常常見, 泛型有助於解決這一難題。

enum Item {
    Inventory(String),
    // None represents the absence of an item
    None,
}

struct BagOfHolding {
    item: Item,
}


Option
Rust 有一個內建的泛型列舉叫做 Option,它可以讓我們不使用 null 就可以表示可以為空的值。

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

這個列舉很常見,使用關鍵字 Some 和 None 可以在任何地方建立其例項。

Result
Rust 有一個內建的泛型列舉叫做 Result,它可以讓我們返回一個可能包含錯誤的值。 這是程式語言進行錯誤處理的慣用方法。

enum Result<T, E> {
    Ok(T),
    Err(E),
}
注意我們的泛型有多個用逗號分隔的引數化的型別。

這個列舉很常見,使用關鍵字 Ok 和 Err 可以在任何地方建立其例項。

main 函式有可以返回 Result 的能力!

fn do_something_that_might_fail(i: i32) -> Result<f32, String> {
    if i == 42 {
        Ok(13.0)
    } else {
        Err(String::from("this is not the right number"))
    }
}

// 主函式不返回值,但可能返回一個錯誤!
fn main() -> Result<(), String> {
    let result = do_something_that_might_fail(12);

    match result {
        Ok(v) => println!("found {}", v),
        Err(_e) => {
            // 優雅地處理錯誤
            
            // 返回一個說明發生了什麼的新錯誤!
            return Err(String::from("something went wrong in main!"));
        }
    }

   // 注意我們在 Result Ok 中使用了一個單位值
     // 表示一切都很好
    Ok(())
}



優雅地錯誤處理
Result 如此常見以至於 Rust 有個強大的運算子 ? 來與之配合。 以下兩個表示式是等價的:

do_something_that_might_fail()?
match do_something_that_might_fail() {
    Ok(v) => v,
    Err(e) => return Err(e),
}


Vectors
一些經常使用的泛型是集合型別。一個 vector 是可變長度的元素集合,以 Vec 結構表示。
比起手動構建,宏 vec! 讓我們可以輕鬆地建立 vector。
Vec 有一個形如 iter() 的方法可以為一個 vector 建立迭代器,這允許我們可以輕鬆地將 vector 用到 for 迴圈中去。
記憶體細節:

  • Vec 是一個結構體,但是內部其實儲存了在堆上固定長度資料的引用。
  • 一個 vector 開始有預設大小容量,當更多的元素被新增進來後,它會重新在堆上分配一個新的並具有更大容量的定長列表。(類似 C++ 的 vector)


fn main() {
    // 我們可以顯式確定型別
    let mut i32_vec = Vec::<i32>::new(); // turbofish <3
    i32_vec.push(1);
    i32_vec.push(2);
    i32_vec.push(3);

    // 但是看看 Rust 是多麼聰明的自動檢測型別啊
    let mut float_vec = Vec::new();
    float_vec.push(1.3);
    float_vec.push(2.3);
    float_vec.push(3.4);

    // 這是個漂亮的宏!
    let string_vec = vec![String::from("Hello"), String::from("World")];

    for word in string_vec.iter() {
        println!("{}", word);
    }
}

相關文章