此篇筆記主要以資訊工程研究所(在已經撰寫了幾個月的 C 語言之後)學習 Rust 的日誌
此外,所有學習紀錄都會放在我的 github 當中以供想要學習 rust-lang 的人觀看。並且科技詞彙增進自身的漢語表達能力。
和所有程式語言一樣,Rust 也有一個主程式,通常叫做 main。而就像在學習所有新語言的朋友們,我們還是必須要形式上的跟世界說嗨!
首先可以定義一個新的檔案叫做 hello.rs
,並且使用簡單的 command (注意!這邊假設已經安裝並且可以使用終端機來編譯 rust 語言,若不知道的人可以看這裡)
fn
來定義而不是和 C 語言一樣必須要求回傳的檔案格式。println!
最後面的驚嘆號,Rust 官方解釋是這是一個巨集(macro)的展開。(將在更後續詳細探討)定義好了之後就可以使用 rustc (c for compile) 來編譯 rustfile
Rust 有一些規範,例如不是用 [TAB]
來縮行,而是使用四個空白
另外,rust 語言把編譯以及執行分開,這樣一來不需要在電腦裡面安裝 rust 也可以執行相對的檔案。
.pdb
的檔案,rust 官方文件說這個檔案存在除錯資訊(目前沒有細說)當專案越來越大時,勢必需要有管理專案的工具。而 cargo 這個工具(在安裝時 rust 時即安裝好)可以幫忙下載函式庫以及協助專案管理。
使用以下指令即可利用 cargo 做出一個 rust 的專案
使用 tree
指令可以看到以下的空專案內容,可以看到專案使用 TOML 來管理,以及在 src 目錄下可以看到一個 main.rs
(預設為先前的檔案)
.toml
檔先來打開檔案來瞧瞧
首先看到 [package]
,也就是當前的專案, [dependencies]
也就是用到的套件都會登記在這邊
cargo
這邊要注意 cargo 專案中的程式碼都必須放在 src
目錄下
而在專案當中也只需要簡單的一行,cargo 即可幫忙把所有檔案連結起來,以下為範例
(cargo 在沒有更改原始碼的狀態下不會重新編譯,基本上就是全部都已經撰寫好的 Makefile
)
<project>/target/debug/
這個目錄底下。根據此教程實作出一個猜數字遊戲。我覺得直接看程式碼來理解 rust 會比較快
上面這個程式碼最重要的地方無外乎為第 9 以及 11 至 13 行。以下來稍微解釋這幾行到底做了些甚麼
Rust 中存在著可變動以及不可變動的變數,let
宣告一個變數,而 mut
讓這個變數可以在後續被修改,而 =
把後面的資訊綁定給這個關鍵字。廢話不多說,馬上來實驗看看,以下為例子:
可以看到第一行 let
定義了一個變數為 a
,而這個變數在第二行的時候我嘗試賦值給他,接下來使用 cargo check
就會跑出以下錯誤訊息:
可以看到 rust-compiler 提示沒有 mut
的資料無法附值兩次。只需要改成 let mut a = 1
就可以更改值了。
再來注意到剛剛第 12 行的部分,&mut guess
,跟 C 語言一樣,加入 &
字符就代表 pass by reference,也就是把記憶體位置送進 function 中,但和其他語言不一樣的是:送進去也需要考慮這個東西是不是可變的 mut
,也就是說,如果把 &guess
替換成原本的引數(在這個情況下會出問題)
這個會在 Lesson-4 中提到
再來看到最後的 .except()
,手冊上提到這個是用來除錯使用的程序,當做完 read_line
之後,內部會有一個 Result,這個 Result 主要會有兩大項目,一個是 OK
,另外一個是 Err
(作用有點類似 Errno)
最後就是 std output,這邊不多敘述,因為 {}
的作用與 C 語言中針對型別的 %d, %ld ...
差不多。
額外補充一點,
cargo
這個工具還可以直接把當前dependency
中的所有文件利用網頁顯示給你看(包括範例)只需要在當前目錄下使用cargo doc --open
,如此一來所有資訊都會在你面前顯示,這樣也不用擔心不知道要怎麼用。
接下來就是比較的時間了,以下程式碼為官方文件的程式碼。
但以上的程式碼沒有辦法過編譯,由於先前輸入的 guess
是一個字串,而這邊放入的 secret_number 為整數,故無法做比較。值得在意的是這邊的 Ordering
以及 match
,他是一個 enum
(Lesson-6/18 會細講),這個 enum
讓我們確保可以 handle 各種狀況。
在不同程式語言中,解析文字並且得到自己想要的資料是一個非常重要的事情。如同 python 給予的簡易 __call__
讓我們可以在各種型別做不同的轉換,例如:
如此在 C 語言非常困難的型別轉換,在這邊可以直接利用以下程式碼的 : <type> = ***.parse()
來轉換
以上示範了怎麼樣用字串做轉型,等等 Lesson3
會細說,這邊只需知道拿來做轉換即可。
最後,我們當然須要讓使用者不停輸入,這樣才可以讓使用者不停的猜,直到猜對。那勢必要把迴圈拿出來用了,以下為 loop
的範例
作用與 while(1)
是相同的。再來只需要當猜中數字的時候跳脫迴圈即可
如此一來,當猜中數字之後就可以跳脫迴圈。
這邊可以注意到第 3 行的部分,把後面的 except (也就是錯誤時跳出錯誤並且停掉主程式) 而改成 match
讓後續回傳剛剛所說的 enum
,如此一來我們就可以處理錯誤,而不是跳脫錯誤了。
至此,我們已經完成了 rust 的第一個猜數字專案。
這個章節會特別注重於型別以及相關規範
在前一個章節我一直有一個疑問,為甚麼宣告變數不用型別,為甚麼要分可變動 (mutable) 跟不可變動 (immutable),而且為甚麼存在不可變動變數時,又存在常數 (const):原來是為了使編譯更快速,以及讓程式開發者更好的去調整
在 Rust 的命名邏輯中,常數通常以 全大寫、中間以底線分開,這兩個規則命名,當定義了常數例如:
編譯器會將這個結果 10800
給定義為常數,而執行時也不會真正執行三次乘法(浪費時間)
將常數定義在好的位置有助於解釋程式碼以及加速開發
變數又分為可變動以及不可變動(先前已經提過),而跟 const 最大的不同在於 shadowing,有點類似重新宣告。
我們可以用以下程式碼來測試看看 var 跟 const 的差別
這樣沒有辦法編譯,原因就是因為這個 THREE_HOURS_IN_SECOND
並不是變數,他沒有辦法再這個 scope
下被 shadowing
讓我們用另外一個形式試試看
這樣就可以編譯成功了,但還是不建議將變數命名成常數的樣子
整體來說 let
讓我們可以重新定義這個變數而不會
Rust 比 C 好的一點即利用 let 可以宣告所有類型的變數(看你怎麼樣賦值),而也可以和 compiler 宣告清楚這個型別:
上述的四行會做到一樣的事情,就看程式設計者想要怎麼設計了。
有很多寫法與 C 語言雷同,這邊不會贅述,以下只會給予不同型別宣告的範例
至此,我覺得最困難的就是 function,因為有非常多種宣告方法。這邊是語法最容易搞混的地方了。所以接下來我都會盡可能的用解釋帶例子讓讀者熟悉寫法。
fn
的關鍵字就可以宣告一個 function到此為止應該會有人想問,為甚麼沒有回傳任何值,那是因為在 rust 當中並沒有 void function,在每一個 function 都必須要有一個箭頭定義他的回傳型別
比較特別的就是在一個花括號的 scope
當中,預設最後一個 expression
為回傳值
所以可以製作出下方的特殊函式
這兩個函式做了完全一樣的事情,回傳 0,但第五行為這個花括號的 scope
當中最後一個 expression,所以預設為回傳值
也可以用這個方法來定義變數
同理,花括號當中最後一個 expression 為回傳值,所以 y 理當為 20
終於來到怎麼樣撰寫 if else
了,接下來會介紹語法以及特殊寫法。
和 C 語言最大的不同點為,expr 必須要布林值 (bool
),而不是大於等於 1 就為 true
。
You must be explicit and always provide if with a Boolean as its condition.
到目前為止,我們學到了一個 scope 是可以有回傳值的,我們馬上來應用一下(這是到目前為止我覺得最刺激最好玩的部分)
這邊應該可以猜到會出現什麼樣的東西了 (可以利用這種定義方法做出寫出很好玩的 code)
loop
loop
這個關鍵字定義了類似 while true
的行為,但又和後者不同,最特別的點在於可以回傳值,也就是在 break 後面接上一個要回傳的值,可以當作這個 scope
的 return value
再來另外一個特別的地方就是,break
也可以當作多層迴圈中用來控制流程的關鍵字,以下為例子:
第一次看到的人一定會覺得很奇怪,為甚麼第 3
行長成這樣?這邊有點類似 C 語言中的 goto,在其他語言若要跳脫多重迴圈,比較簡易的方法是使用 goto, return
或是用多個 if else
去管理,後者會導致寫出來的程式碼缺少了美感並且不容易懂。從這邊即可得知這個 break
以及 label
的設計意義了。並且從剛剛的教學可以看到,break
後面可以接一個回傳值,那在這種跳脫多層迴圈的狀況呢?
非常簡單,只需要在 label
後面放入回傳值即可
若把前面的第 13 行改為以上的樣子,就可以在這個 scope 當中回傳 777(不過還是記得要把它改成變數型式)
while
for
就與大部分程式語言一樣,當 while
後方的 condition 為真時,會不停的跑內部的行為
而 for
的語法反而與 python
雷同
輸出如下:
如此方便的語法,可以協助我們爾後在不確定 array 長度時遍歷整個 array
這個 section 的最後,我想要講一下 ..
的功能
這邊的 ..
其實就是把頭尾之間的所有整數展開,幫我們更好定義了變數會走動的範圍。