Rust 的集合
Rust 的標準函式庫有一些非常實用的資料結構,稱之為集合(collections)。這些集合包含了一些常見的資料結構,例如 vector、hash map、linked list 等等。這些集合都是泛型的,所以可以用來存放任何型別的資料。
向量(Vector)vector 是一個可以動態增長的陣列。它的實作是用一個 buffer 來存放資料,當 buffer 不夠用時,會自動增長。vector 的實作是用 unsafe Rust 來實現的,所以它的效能比較好。
建立一個 vector建立一個空的 vector 可以這樣做:
1let mut v: Vec<i32> = Vec::new();
或者這樣:
1let mut v = vec![];
增加元素可以用 push 方法來增加元素:
123v.push(1);v.push(2);v.push(3);
讀取元素可以用索引的方法來讀取元素:
123let third: &i32 = &v[2];println!("The third element is {}" ...
特徵
特徵(Trait)是 Rust 的一個重要的特性,它可以讓我們在不同的型別上定義共用的行為,並且可以在不同的型別上使用相同的函式。特徵有點像是其他語言的介面(interface),目前我們可以先把它想成是一種定義共享行為的方式。
由於我在學特徵這部分的時候,覺得這個概念滿抽象的,所以我會盡可能的以不同的例子來展示,希望能讓大家更容易理解。(標題越來越懶的想XD)
定義特徵要怎麼定義一個特徵呢?
其實很簡單,只要在 trait 這個關鍵字後面加上特徵的名稱,然後在大括號裡面定義特徵的行為就可以了。
123trait Summary { fn summarize(&self) -> String;}
這是一個基本的特徵,在上面的程式碼中,我們定義了一個名為 Summary 的特徵,裡面有一個名為 summarize 的函式,它會回傳一個 String。
再講的仔細一點,定義一個特徵就是:
把方法的型態簽章放在一起,來定義實現某種目的所必須的一組行為。
分別有以下幾個重點:
關鍵字:trait
只有方法的型態簽章,沒有具體實現
方法的型態簽章:就是 ...
泛型
什麼是泛型?在寫程式中,可能會遇到不同需求,例如想要得到多種類型的結果,像是結果型別為 i32,又或是浮點數 f64 等等…。這對工程師來說根本是種折磨,因為工程師就是懶,要寫相同的過程,但結果只是不同型別的話,就像下面這樣:
123456789fn result_i8(x: i8, y: i8) -> i8 { x + y}fn result_i32(x: i32, y: i32) -> i32 { x + y}fn result_f32(x: f64, y: f64) -> f64 { x + y}
還好 Rust 提供了泛型(generics)。
泛型可以提高程式碼的重複使用,也就是如果程式碼中有很多重複的部分的話,泛型就可以處理重複程式碼的問題。
泛型函式定義在使用泛型定義函式時,只需要在函式名稱的後面加上 <T>,T 代表泛型參數,雖然也可以把 T 換成任一字母,但慣例會用 T 來表示 Type,就像這樣:
123fn result<T: std::ops::A ...
結構
結構(struct)是 Rust 提供的一種可以讓開發者建立資料型別相對複雜的一種方式,類似 JavaScript 的物件和 Python 的類別。
結構和元組類似,都是可以由不同不同型態組成,不過與元組不同地方在於必須要為每個組件命名,所以可以不必按照順序,就可以讀取或修改結構的個別資料。
Rust 提供了三種結構,分別是:
具名欄位(named-field):最常用的用法,每個組件都有一個名稱。
類元組(tuple-like):因為類似元組,所以也必須要按照順序使用。
類單元(unit-like):比較不常見,其結構完全沒有組件。
具名欄位直接看範例如何建立一個具名欄位的結構:
123456struct Hunter { name: String, nen_type: String, age: u8, hunter_license: bool,}
首先起手式先用關鍵字 struct 為整個結構命名,命名的方式為駝峰命名(Camel Case),然後在大括號中加入欄位,欄位的名稱則是使用蛇式命名(snake_case),接著就幫每個欄位 ...
參考和借用
上一篇提到的所有權,在變數的所有權更換時,原本的變數所有權也跟著解除。這讓 Rust 在使用變數的時候跟其他程式語言比較起來,就稍微顯得麻煩。
參考不過 Rust 也提供了相對應的機制可以解決這個問題,也就是參考(reference)。直接看下方的範例:
12345678let x = "abc".toString();do_some(&x);println!("{}", x);fn do_some(s: &String) { println!("{}", s);}
在這個例子中,我們建立了一個變數 x,型別是 String,然後也建立了一個函式,參數的型別也是 String。看起來跟上一篇的範例差不多,不過這裡有一些小小的差異。不同之處在於參數以及型別加上了 & 符號,這個符號就是參考,而參考的使用就是讓開發者不必變更所有權就可以使用其變數的值。這時候運行就不會報錯,並且可以成功印出值。
雖然 & 可以讓開發者建立一個指向變數的參考 ...
記憶體管理機制 - 所有權
所有程式語言都有自己的一套管理記憶體的方式,有些語言使用垃圾回收機制(GC),有些則是讓開發者自行分配和釋放記憶體。而 Rust 則是選擇了另一條路,記憶體是由所有權系統管理,並且在編譯期會檢查是否有違反規則,如果有的話,程式就無法編譯。
所有權是 Rust 中非常獨特的概念,因為它讓 Rust 不用像其他語言一樣需要 GC,這樣的作法也讓 Rust 的效能更好。
所有權所有權主要有三個規則:
每個值都有變數作為一個擁有者。
值只有一個擁有者,任何變數都不能共有擁有權。
當擁有者超出作用域時,該值會立刻刪除。
我們直接來看範例:
123let x = "Rust".to_string();let y = x;println!("{}", x);
首先,我們宣告了一個變數 x,並且將 “Rust” 轉成 String 類型,並且將它賦值給 x。
接著,我們宣告了一個變數 y,並且將 x 賦值給 y。
這時候,x 和 y 都是 String 類型,並且都擁有了 “Rust”。
接著,我們將 x 印出來,這時候會發生錯誤,因為 ...
重複的事就讓迴圈來吧
當要處理比較重複的事情時,我們總不可能一直重複寫一樣的程式碼,這時候我們就可以透過一個迴圈來幫我們處理這件事情。而 Rust 中的迴圈主要分為三種,分別是:
loop
while
for
looploop 這個迴圈是一個無限迴圈,也就是說,只要沒有遇到 break 或 return,這個迴圈就會一直執行下去。
一個最簡單的 loop 迴圈如下:
123456fn main() { loop { println!("Hello, world!"); break; // 如果沒有加上 break,這個迴圈就會一直執行下去 }}
但 loop 如果只單純這樣寫的話,好像不太實用。所以我們就可以利用昨天學到的 if...else 加上判斷的條件,來自己決定什麼時候該停止這個迴圈。
如果我們想要用一個迴圈,來處理從 1 數到 10,那麼就可以這樣寫:
12345678910fn main() { let mut count = 1; loop { ...
控制流程 if…else
不知不覺中我們也一起學到了第 10 天,已經達成三分之一了。(ง๑ •̀_•́)ง
今天來介紹的是 Rust 的條件判斷,相信有學過其他程式語言的人應該都知道,條件判斷是用來判斷程式執行的流程,而在 Rust 中,條件判斷的語法跟其他語言也差不多,但還是有一些地方需要了解一下。
if…else在 Rust 中寫 if…else 非常簡單,不需要在條件加上 (),因為在 if 和 { 中的範圍都是代表條件,這也就表示在這範圍內寫的條件必須是 true 或 false,而且在大多數情況下並不需要加入型別。
以下是一個簡單的範例:
123456789101112131415fn main() { let result = print_result(20, 10); println!("{}", result); // ?}fn print_result(x: i32, y: i32) -> char { let msg; if x > y { ms ...
Rust 的字串
前面雖然在 Rust 的基本型別有提到字元,但是由於字串跟字元不太一樣,而且覺得蠻有趣的,所以本篇將會專門介紹字串。
字串常值字串常值的使用方式跟字元常值不一樣的地方在於,是使用 "" 來將文字包起來,看起來就像這樣:
1let msg = "Hello World!";
也就是說 msg 的值就是一個字串常值(string literial),但只要先記住只要用 "" 包起來的就是字串常值就好了,因為接下來還有比較複雜的部分要稍微理解一下。
Rust 的字串是一種編碼成 UTF-8 的 Unicode 數值,並且一共有 6 種型別,但最主要以及最常用的有兩種,分別是:
&str
String
String首先 String 可以經由字串常值加上 to_string() 宣告,或者是使用以下方式宣告:
1let x = String::from("This is a pen!");
字串以我自己的用途來說,比較多用在組合字串上,那這邊來介紹一下幾種作法,讓大家參考一下。
用 + 來組合這應該 ...
Rust 的複合型別
在 Rust 中,有些型別是由其他型別組合而成的,這些型別稱為複合型別,這些複合型別可以讓我們更好的處理資料,讓程式更簡潔。
元組(tuple)元組有固定長度的特性,代表著一但宣告完,之後就無法再增加或刪減。建立元組的方法是用一個 () 將值放入,每個值之間用 , 分隔,不同值不限定相同型別:
1let nums = (1, 4.4, 333);
雖然不一定要加上型別,但以下為加上型別的範例:
1let nums: (u8, f64, i32) = (1, 4.4, 333);
兩種方法可以取出 tuple 的值:
1.1234567fn main() { let nums: (u8, f64, i32) = (1, 4.4, 333); let one = nums.0; let two = nums.1; let three = nums.2; println!("{}, {}, {}", one, two, three);}
2.12345fn ...