Rust 語言學習之旅(2)
列舉
列舉允許你使用 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); } } |
相關文章
- Rust 語言學習之旅Rust
- Rust 語言學習之旅(7)Rust
- Rust 語言學習之旅(6)Rust
- Rust 語言學習之旅(3)Rust
- Rust學習之旅2——常見程式設計概念Rust程式設計
- 從Julia到Rust語言的學習 - miguelrazRust
- Go語言學習(2) - HelloWorldGo
- Go語言HTTP/2探險之旅GoHTTP
- Jon Gjengset認為學習Rust語言並不難GseRust
- 這些情況會阻礙你學習Rust語言 - dystroyRust
- C語言學習方法,怎麼學習C語言?C語言
- rust學習四、控制語句Rust
- 學習Rust 基礎語法Rust
- 學習Rust 條件語句Rust
- Flutter學習筆記(2)--Dart語言簡介Flutter筆記Dart
- 嵌入式C語言學習筆記2C語言筆記
- go語言學習Go
- java語言學習Java
- Rust學習之旅1——寫個猜數字遊戲Rust遊戲
- 重新學C語言2C語言
- 【Go】Go語言學習筆記-2-函式Go筆記函式
- Solidity語言學習筆記————2、使用編譯器Solid筆記編譯
- Rust語言4歲了 | rust-langRust
- Rust之旅 02.通過例子學習自定義型別Rust型別
- rust學習九.2、集合之字元Rust字元
- Go語言學習——mapGo
- C語言再學習C語言
- C語言學習心得C語言
- go語言學習-介面Go
- go語言學習-goroutineGo
- Go語言學習查缺補漏ing Day2Go
- ROS2學習之旅(1)——初識ROS2ROS
- 8.17----學習JAVA語言Java
- 7.27----學習JAVA語言Java
- c語言基礎學習C語言
- GO語言學習——切片二Go
- C語言指標學習C語言指標
- go 語言指標學習Go指標