[Rust] 程式設計教學
試用 Rust
如果只是想先嘗試一下 Rust 的語法,可到 Rust Playground 網站,即可線上撰寫 Rust 程式,不需額外安裝其他的軟體。由於安全性考量,這個網站不能使用外部套件,只能用 Rust 內建的套件,對於初期的練習這樣的環境已經足夠。
安裝 Rust
Rust 本身是跨平台的程式語言,同時支援 Windows、Mac、Linux、BSD 等多種平台,目前安裝和管理 Rust 主要是透過 rustup 這隻終端機程式。rustup
的優點在於可以很輕鬆地在不同版本和平台的 Rust 編譯器間切換,對於更換不同 Rust 版本和交叉編譯 (cross-compiling) 相當方便。
在 Windows 下安裝 Rust
在 Windows 系統中,根據不同的 C 和 C++ 編譯器,對應不同的 Rust 版本,一個是使用 Visual Studio 的 MSVC ABI (Application Binary Interface),一個是使用 GCC 編譯器的 GNU ABI,要使用那一個版本的 Rust,要看使用者想使用那個版本的 ABI 所建立的函式庫而定。
若讀者沒特別的偏好,建議以 GNU ABI 較佳,原因在於日後需要引用第三方 C 或 C++ 函式庫時,使用 GNU ABI 可以搭配 MinGW/MSYS2 所提供的第三方 C/C++ 函式庫。
在類 Unix 系統下安裝 Rust
對於 Mac 和 Linux 等類 Unix 系統,rustup 提供 shell 命令稿來安裝。在終端機輸入以下命令:
$ curl https://sh.rustup.rs -sSf | sh
rustup
會出現提示文字,可視需求自行調整,若沒有特別需求,接受預設選項即可。 rustup
和 Rust 編譯器都會安裝到 $HOME/.cargo/bin 資料夾,再將此路徑加入 PATH 變數即可。
使用 rustup 管理 Rust
透過 rustup
可管理多個版本的 Rust,相當方便,本節介紹 rustup
的使用方式。
Rust 有三個不同的版本,分別為 stable、beta、nightly,有一些實驗性質的特性,僅放在 nightly 版本的 Rust。預設情形下,rustup 會安裝 stable 版本的 Rust。輸入 rustup update
指令即可更新 Rust:
$ rustup update
info: syncing channel updates for 'stable'
info: downloading component 'rustc'
info: downloading component 'rust-std'
info: downloading component 'rust-docs'
info: downloading component 'cargo'
info: installing component 'rustc'
info: installing component 'rust-std'
info: installing component 'rust-docs'
info: installing component 'cargo'
info: checking for self-updates
info: downloading self-updates
stable-x86_64-unknown-linux-gnu updated - rustc 1.14.0 (e8a012324 2016-12-16)
如果要更新 rustup
,輸入 rustup self update
。
$ rustup self update
info: checking for self-updates
info: downloading self-updates
切換 toolchain
Rust 有許多針對不同 CPU、系統、C/C++ 函式庫的版本,透過 rustup
可快速地切換。先以 rustup toolchain list
列出所有可選的版本:
$ rustup target list
# Omit some message
x86_64-apple-darwin
x86_64-apple-ios
x86_64-pc-windows-gnu
x86_64-pc-windows-msvc
x86_64-rumprun-netbsd
x86_64-unknown-freebsd
x86_64-unknown-linux-gnu (default)
x86_64-unknown-linux-musl
x86_64-unknown-netbsd
musl 是一套小型的 C 函式庫,可用來在 Linux 下建立靜態連結的機械碼。若想安裝以 musl 為基礎的 Rust,使用 rustup target add
:
$ rustup target add x86_64-unknown-linux-musl
日後在編譯專案時,可指定特定的 target:
$ cargo build --target=x86_64-unknown-linux-musl
使用實驗性質的特性
某些尚在實驗性質、不穩定的特性,會在 nightly 版本的 Rust 發布。若想使用 nightly 版本的 Rust,輸入 rustup install nightly
:
$ rustup install nightly
info: syncing channel updates for 'nightly'
info: downloading toolchain manifest
info: downloading component 'rustc'
info: downloading component 'rust-std'
info: downloading component 'rust-docs'
info: downloading component 'cargo'
info: installing component 'rustc'
info: installing component 'rust-std'
info: installing component 'rust-docs'
info: installing component 'cargo'
nightly-x86_64-unknown-linux-gnu installed - rustc 1.15.0-nightly (71c06a56a 2016-12-18)
安裝完後,可輸入 rustup default
切換 Rust 版本:
# Use nightly version
$ rustup default nightly
# Use stable version
$ rustup default stable
在實務上,除非需要某個穩定版本缺乏的語言特性之外,較不建議使用 nightly 版本的 Rust,會造成專案程式碼的不穩定。
由於 Rust 的函式庫是跨平台的,理論上,交叉編譯也是可行的。假若想要在 Linux 上交叉編譯某個 Rust 專案到 Mac 平台,在該專案的根目錄輸入以下指令:
# Say that we are on Linux now
$ cargo build --target=x86_64-apple-darwin
註:經筆者實際測試,交叉編譯有時仍有問題需自行排除,和該平台的 C/C++ toolchain 有關。
若該 Rust 專案只用到 Rust 程式碼,應該都可以在不同系統間編譯。若有用到第三方 C/C++ 函式庫,則需自行建置交叉編譯的環境。可參考 crosstool-NG 或其他的方案來建立環境,但交叉編譯是較進階的主題,本書不詳述,請讀者自行查閱相關資料。
反安裝 Rust
如果因某些因素,決定不繼續使用 Rust,可以用以下指令反安裝 Rust 和 rustup:
$ rustup self uninstall
use std::io::stdin;
fn main(){
let mut input = String::new();
stdin().read_line(&mut input);
println!("Hello, World.\n{}", input);
}
- 首先我們可以先發現在 Rust 裡頭,分號是必須的唷!這點跟很多 Modern languages 不同,你可以說有點麻煩,但好處是讓可讀性能夠增加,表示一個表達式的結束,因為 Rust 是 expression based (雖然我已經習慣沒有打分號的日子)。
- Rust 透過
use
來將其他的 Module的 Function 引入,讓我們在使用 Function 時可以不用將整個路徑寫出來。在 Rust 定義 Function 的關鍵字是fn
。let
是做變數綁定,為什麼我特別說是變數綁定而不是傳統的變數賦值?這裡就要講到 Rust 為了記憶體的安全性,而有一個很重要的觀念就是 Ownership (所有權)。如果有學過 C/C++,相信對 Pointer 一定不陌生 (或是頭痛?),而 C/C++ 因為讓你有很高的自由度可以操作 Pointer,所以就會有機會產生 Dangling pointer,也就是假設 A 和 B 都是指向同一個記憶體區塊的 Pointer,但是當 A 執行釋放記憶體後,想再用 B 去存取就會產生錯誤。而 Rust 則是引入了 Ownership 的觀念來解決這個問題。簡單來說,每份資料同一時間只會有一個擁有者,因此假使今天 A 是一個 Reference (可想成是 Pointer),而我們let B = A
,此時所有權就從 A 轉移到了 B 身上,A 就不再能夠存取對應的資料了。但如果 A 是例如一個整數,就不會有這個問題,因為此時的let B = A
,Rust 會拷貝一份資料給 B,所以 A 還是可以存取,與Reference
的情況不同。回到 A 是 Reference 的情況,假使把 A 傳入某個 Function 作為參數,那麼當該 Function 執行完之後,A 也不能存取了,因為在傳入的那刻,所有權就轉移到了該 Function 身上。為了解決這個問題,在 Rust 又有一個 Borrowing 的概念,也就是把所有權暫時借給該 Function,等到 Function 返回之後,所有權又回到 A 的身上,而實務上就是在傳入的時候加上stdin().read_line(&mut input)
的&
囉! 最後就是&mut
的mut
,因為在 Rust,如果要讓轉移後可以有權去修改,那也要明確指出,所以&mut
就是說明可以修改囉!(昏頭了嗎?哈!可以參考 這裡 有更多的說明)!
String::new()
的結果,會是一個新的 Empty string ,並且綁定在 input 這個變數。接著stdin()
會回傳一個 stdin 的 struct,並且我們 invoke 其方法read_line
,而這裡餵入的參數就是上面說明的&mut input
,因為 input 這個變數必須要讓read_line
這個 Function 拿到所有權並且修改,最後就是透過println!
印出來囉!
println!
出現了一個驚嘆號,這代表說這不是一般的函式,而是 Rust 中的巨集,至於 Rust 的巨集又是什麼呢?與像是 C 的 Macro 不同,Rust 的 Macro 不僅僅是文字替換。在 Rust 中, Macro 是用來讓 Compiler 替我們生出程式,Compiler 會先將 Macro 展開,生成程式,再進行編譯。而且 Macro 不會受到一般函數的限制,甚至也不一定要用()
來放入參數,可以使用像是[]
(像是vec![1,2,3]
) 或是{}
都可以。在 Rust 中,一般函數是無法接受任意數量的參數的,也因此println!
勢必要是個 Macro 囉!