# Rust ## Rust tooling and ecosystem - [Rust in the Linux kernel - Google Security Blog](https://security.googleblog.com/2021/04/rust-in-linux-kernel.html) - 不是全部用Rust改寫Linux kernel,目標是能夠用Rust能夠zero-cost使用Kernel的C API、原有C撰寫的Kernel能使用以Rust寫的新功能 - 有semaphore使用C和Rust的比較範例 (還沒看) - Android已支援Rust開發OS本身 - [Rust for Linux - Github](https://github.com/Rust-for-Linux/linux) - [Linux kernel modules in safe Rust - Github](https://github.com/fishinabarrel/linux-kernel-module-rust) - [我眼中的 Rust 2020:生態工具發展](https://weihanglo.tw/posts/2020/rust-2020-tooling-ecosystem/) - 各種工具和專案 - [Rust for C++ programmers](https://github.com/nrc/r4cppp) - A Rust tutorial for experienced C and C++ programmers. - [【譯】Rust vs. Go](https://weihanglo.tw/posts/2018/rust-vs-go/) - 強力推薦在座的各位學習 Rust,與 borrow checker 與 ownership 和平共處。即使最終你並沒有使用 Rust,過程中所學都會回饋到其他語言上,連 Go 也不例外。 ## Rust 語法簡介 - [給 C++ 使用者的 Rust 簡介 - electronic.blue](https://electronic.blue/blog/2016/10/10-rust-an-introduction/) (有一系列簡要的文章) - Rust 的目標是成為高效率、易於平行運算的系統程式語言,因此它選擇了以下的特性: - 靜態型別 (static-typed) - 區分 mutable 與 immutable,所有變數預設為 immutable,盡可能減少 mutable state - 使用 tagged union 與 pattern matching - 不使用動態垃圾回收 (garbage collection),而使用靜態的 RAII - 使用 Move semantics 避免複製物件 - 使用 borrow checker 確保 memory safety 與 thread safety - Move Semantics - Rust 並沒有 class,那麼 rust 的 struct 是 value-type 還是 reference-type 呢?我們試著用最簡單的做法來判定 value-type 與 reference-type:宣告一個物件,用等號賦值給另一個物件並修改其內容,然後檢查原物件的值是否變動。對 value-type 來說是不變動的,而對 reference-type 來說,因為兩個變數實際指向同一塊記憶體,因此內容會變動。然而,這兩種狀況都不會發生在 Rust 上面,因為 compiler 把它擋下來了。 ```Rust= struct Point { x: i32, y: i32, } fn main() { let mut foo = Point { x: 10, y: 20 }; let mut bar = foo; bar.x = 30; println!("foo.x = {}", foo.x); // error: use of moved value `foo.x` } ``` - 為了使用上的方便,Rust 的基本型別,都具備可複製的特性。因此使用等號賦值時,進行的動作是「複製」,讓你可以繼續操作右值。 - 表達式 - Rust 是 expression-oriented language,大部份的流程控制結構,比如說 if,其實都是可以求值的表達式。 - 分號可以用來分隔表達式,最後一個不帶分號的表達式會成為整個表達式的結果,因此 is_leap 會根據條件判斷,成為 "is" 或 "is not"。注意第七行與第九行都不能加分號,要是最後一個運算式也加上分號,那麼整個運算式的結果會變成 (),也就是那個沒啥用的 0-tuple。而在第 11 行的分號則用來區隔 let 變數宣告與 println!,是一定要加上去的。 > 為何表達式會變tuple? ```Rust= let year = 2016; let is_leap = { let div_4 = (year % 4 == 0); let div_100 = (year % 100 == 0); let div_400 = (year % 400 == 0); if div_400 || (div_4 && !div_100) { "is" } else { "is not" } }; println!("Year {} {} a leap year.", year, is_leap); ``` - 函式 - 函式本體也是可以使用分號區隔的表達式,最後一個不帶分號的表達式會自動成為函式的回傳值,因此上一段檢查閏年的函式可以這樣寫: ```Rust= fn is_leap(year: i32) -> bool { let div_4 = (year % 4 == 0); let div_100 = (year % 100 == 0); let div_400 = (year % 400 == 0); div_400 || (div_4 && !div_100) } ``` - 即使函式不回傳任何值,它還是有回傳型別,也就是上面提到那個好像沒啥用的 0-tuple。這看起來好像沒什麼用,畢竟 0-tuple 什麼事都做不了。然而,當你要寫泛型函式 (generic function) 時,你會跪在電腦前感謝這個設計。 ```Rust= fn say_hello() -> () { // -> () 可省略 println!("hello world"); } fn main() { // 若無回傳型別,rust 會自動加上 -> () let result = say_hello(); // 合法,result 的值為 () } ``` - 泛型 - 如同 C++ 那般,Rust 也可以利用模版 (template) 來達成泛型程式設計,語法也非常接近 C++。與 C++ 不同的是,大部份情況下 Rust 都能藉由前後文來自動推導出正確的模版型別,因此上面的例子中並不需要特別加入 \<i32> 或是 \<f64>,直接用 Point 即可。 ```Rust= struct Point<T> { // 相當於 template<typename T> struct Point x: T, y: T, z: T, } fn main() { let point_i32 = Point { x: 10, y: 20, z: 30 }; let point_f64 = Point { x: 2.078, y: 0.454, z: 3.1415 }; } ``` - 泛型非常適合用來實作容器型別,比如 Rust 提供的 Vec 泛型容器,就相當於 C++ 的 std::vector。同樣地,因為 Rust 從第三行的 push(1) 判斷出 array 的元素型別為 i32,因此在第二行就不需要寫明 Vec\<i32>::new(),直接寫 Vec::new() 即可。 ```Rust= fn main() { let mut array = Vec::new(); array.push(1); array.push(2); println!("{}", array[0] + array[1]); } ``` - 除了泛型類別,模版也可以用來定義泛型函式,然而與目前 C++ 不同的地方是,在 Rust 中,對泛型型別進行操作前,必需為它標上 constraint。這邊 Add 意指 T 必需是可以使用加號相加的型別,包括整數及浮點數都包括在內。由於相加後輸出型別不一定仍然為 T,因此這個函式的回傳型別是 T::Output。Rust 也支援運算子覆載 (operator overloading),只要你的自訂型別定義了加號操作以及輸出型別,那麼這個自訂型別也可以直接傳入 sum 進行運算。 > 很像C++ 20的concept ```Rust= fn sum<T: Add>(a: T, b: T) -> T::Output { a + b } ``` - [Rust基礎概念 - Michael Chen](https://michaelchen.tech/rust-programming/intro/) - Rust的安全性 - 而 Rust 中,透過嚴格的編譯器,將許多 C 或 C++ 中相對危險的操作視為程式的錯誤,但仍保有一些操作指標的自由。嚴格的編譯器使得 Rust 較難上手,以前在程式執行時發生的錯誤,現在提早到編譯時就發現。往好處想,當程式能順利編譯時,該程式的錯誤也比較少。 - Rust 的效能 - 一些評效 (benchmark) 顯示,在相同演算法的前提下,Rust 程式已有接近等效 C++ 程式的執行速度。 - 在實務中,已經有人用 Rust 撰寫遊戲引擎 (game engine) (如 Piston) 和作業系統 (operating system) (如 Redox),而這些項目都對效能有較高需求,可以知道 Rust 已有足夠的效能和低階操作,應對不同層面的任務。 - 在多核處理器中使用 Rust - Rust 同樣將安全的特性放入共時性程式中,讓程式設計者在安全的前提下,撰寫共時運算的程式。 - Rust 的應用範圍 - 使用 C 或 C++ 仍然要去面對平台間的差異,所以才需要撰寫條件式編譯相關程式碼。撰寫 Rust 程式碼時,不用再撰寫條件式編譯的程式碼,也就是說,Rust 程式碼是跨平台的。 - 雖然 Rust 的角色類似 C 和 C++,但是,許許多多以 C 和 C++ 撰寫的函式庫,已經相當成熟穩健,重新以 Rust 實作這些函式庫不是明智的選擇,而 Rust 提供介面讓我們很容易地再利用這些函式庫,減少重造輪子所浪費的心力。 - 使用 Rust 的注意事項 - 因為剛發展的緣故,網路上一些關於 Rust 的文章變成錯誤的資訊,學習者透過網路學習 Rust 時,需多方嘗試。隨著 1.0 版發布,Rust 的核心特性大抵上穩定下來,這個問題逐漸減少。 - 目前的函式庫 (library) 和框架 (framework) 沒有那麼豐富 - Rust vs. C++ - C++ 傾向於在語言層次提供各種豐富的機制,讓使用者從中自由組合出期待的效果,這也使得 C++ 成為一個複雜的語言;Rust 也有豐富的語法機制,不過,Rust 吸收了許多函數式語言的特色,寫起來和 C/C++ 風格的語言有所不同,需要一段時間來適應。 - C++ 將記憶體管理的責任留給使用者,Rust 則自動處理大部份的記憶體操作。比起 C++,Rust 的編譯器較為嚴格,將許多常見的錯誤提前到編譯時期,使得程式更為安全。 - Rust vs. Go - Go 使用一套簡單易學的語法機制,使用者可以很快熟悉大部分 Go 的特性,將其使用在自己的專案,但是,Go 缺乏部分重要的語法特性,為了向下相容,短期內這些問題不會改變;Rust 的語法機制則較完整,但語法不穩定的問題,使得 Rust 函式庫相對不穩定。 - 另外,Go 依賴垃圾回收,使得 Go 較不適合即時運算 (real-time computing) 方面的應用,而 Rust 沒有這個問題。 - [Rust for C++ programmers](https://github.com/nrc/r4cppp) ## Rust 練習題 ### Rust by Example https://doc.rust-lang.org/rust-by-example/index.html > Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust concepts and standard libraries. ### rustlings https://github.com/rust-lang/rustlings > Small exercises to get you used to reading and writing Rust code! ### The Algorithms - Rust https://github.com/TheAlgorithms/Rust > All algorithms implemented in Rust - for education ## Rust 效能 - [The Rust Performance Book](https://nnethercote.github.io/perf-book/title-page.html)