[Rust] 程式設計教學:基礎概念

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    前言

    程式語言在易用及效能的端點間擺盪,Python 或 Ruby 簡單易學,但隨著專案的規模擴大,而顯得效能不佳,C 或 C++ 的效能良好,但充斥著許多不安全的操作,Java 和 C# 同時具有效能和安全性,但需依賴龐大的虛擬機器。程式語言很難兼具效能和安全性,而且要有相對高階的語法。

    Rust 是一個新興的編譯語言,融入部分高階語言的特性,但仍具有接近 C++ 的效能,而沒有 C 或 C++ 中危險的部分。不像大多數的現代高階語言,Rust 不需要虛擬機器,也不依賴垃圾回收。其程式碼轉換成原生的機械碼,使 Rust 具有良好的效能,且易於和其他語言合作。

    目前已經有數個實際的 Rust 應用案例,包括 Servo 網頁瀏覽器、Firefox 網頁瀏覽器元件、Rust 編譯器、Deno 網頁程式平台 (執行 JavaScript 和 TypeScript) 等。甚至已經有人用 Rust 撰寫遊戲引擎及作業系統,可知 Rust 的確有足夠的效能及低階操作可應對不同的任務。

    Rust 的安全性

    在 C 或 C++ 中,使用者有較大的自由,像是可以自由地操作指標和記憶體。然而,C 或 C++ 不是一個安全的語言,有許多的動作是未定義且危險的,程式設計者需維持高度自律,以避開這些問題。在許多高階語言中,程式是安全的,但高階語言的使用者無法像在 C 或 C++ 中那麼自由。

    而 Rust 中,透過嚴格的編譯器,將許多 C 或 C++ 中相對危險的操作視為程式的錯誤,但仍保有一些操作指標的自由。嚴格的編譯器使得 Rust 較難上手,以前在程式執行時發生的錯誤,現在提早到編譯時就發現。往好處想,當程式能順利編譯時,該程式的錯誤也比較少。

    Rust 的效能

    一些評效 (benchmark) 顯示,在相同演算法的前提下,Rust 程式已有接近等效 C++ 程式的執行速度。

    在實務中,已經有人用 Rust 撰寫遊戲引擎 (game engine) (如 Piston) 和作業系統 (operating system) (如 Redox),而這些項目都對效能有較高需求,可以知道 Rust 已有足夠的效能和低階操作,應對不同層面的任務。

    其中的原因之一,在於 Rust 不依賴垃圾回收 (garbage collection) 來管理記憶體,而是使用類似 C++ 中的 resource acquisition is initialization (RAII) 機制,此外,Rust 也不需要透過虛擬機器 (virtual machine) 來運行,其程式碼會轉為原生的機械碼。

    在多核處理器中使用 Rust

    由於物理條件的限制,現在的 CPU 不再以衝高時脈為目標,而朝向多核心發展,能夠支援共時性 (concurrency) 的程式語言,對於程式的效率,會有一定的提升。

    然而,比起一般的程式,共時性程式較難撰寫,程式中也會有更多難以發現的臭蟲 (bug)。Rust 同樣將安全的特性放入共時性程式中,讓程式設計者在安全的前提下,撰寫共時運算的程式。

    Rust 的應用範圍

    近年來,系統程式語言的主流是 C 或 C++,而 Rust 提供相似的角色。然而,這不代表 Rust 不能用來寫應用軟體,就像是 C++ 也可以用來寫應用軟體一般。

    雖然 C 或 C++ 讓我們不再需要為每個特定的平台撰寫組合語言,然而,使用 C 或 C++ 仍然要去面對平台間的差異,所以才需要撰寫條件式編譯相關程式碼。撰寫 Rust 程式碼時,不用再撰寫條件式編譯的程式碼,也就是說,Rust 程式碼是跨平台的。

    跨平台不是什麼新聞,在 Java 之後的程式語言若不能跨平台很難有立足之地,然而,大部分的高階程式語言都依賴某個特定的執行環境 (runtime environment),而 Rust 程式碼在跨平台的前提下,可產生原生機械碼。

    另外,值得一提的是,Rust 和其他語言間的合作。雖然 Rust 的角色類似 C 和 C++,但是,許許多多以 C 和 C++ 撰寫的函式庫,已經相當成熟穩健,重新以 Rust 實作這些函式庫不是明智的選擇,而 Rust 提供介面讓我們很容易地再利用這些函式庫,減少重造輪子所浪費的心力。

    另一方面,某些任務,使用其他高階語言即可完成,也不需要用 Rust 全部重寫,這時候,Rust 的角色就如同 C 或 C++ 一般,為這些高階語言提供延伸模組,在關鍵步驟為程式加速。

    使用 Rust 的注意事項

    由於 Rust 在演進的過程中,嘗試了一些語法特性,但後來又廢棄不用,使得網路上一些關於 Rust 的文章變成錯誤的資訊,學習者透過網路學習 Rust 時,需多方嘗試。隨著 1.0 版發布,Rust 的核心特性大抵上穩定下來,這個問題逐漸減少。

    由於 Rust 還是一個年輕的語言,目前的函式庫 (library) 和框架 (framework) 沒有那麼豐富。使用者不能期待像 Java 或是 Python 般完整的社群資源。在將 Rust 引入自己的專案前,需要評估是否有充足的外部資源,還是需要自行實作。隨著時間,Rust 社群成長,這一點應該會逐漸改善。

    Rust 和其他語言的比較

    程式語言間不存在絕對的優劣,這類討論往往最後流於不同意識型態間的爭執。此外,演算法是獨立於程式語言之外的,不會有某個演算法只能用某個語言來實作的情形,即使某個語言缺乏某些特性,通常可以用其他的方法代替。

    語言的選擇,往往是許多因素綜合考量後的結果。這裡的比較,基於筆者過去的經驗,而帶有主觀的色彩,不是絕對的標準。讀者可再多方收集相關資訊,但不需執著於誰好誰壞。

    vs. C++

    C++ 傾向於在語言層次提供各種豐富的機制,讓使用者從中自由組合出期待的效果,這也使得 C++ 成為一個複雜的語言;Rust 也有豐富的語法機制,不過,Rust 吸收了許多函數式語言的特色,寫起來和 C/C++ 風格的語言有所不同,需要一段時間來適應。

    C++ 將記憶體管理的責任留給使用者,Rust 則自動處理大部份的記憶體操作。比起 C++,Rust 的編譯器較為嚴格,將許多常見的錯誤提前到編譯時期,使得程式更為安全。

    vs. Java

    Java 從一開始就強調物件導向程式設計,將物件導向融入 Java 的語法;Rust 也支援物件導向,但並不特別強調。比起 Java,Rust 的物件導向略為不同,傾向用組合 (composition) 代替繼承 (inheritance) 等。

    出於安全性的考量,Java 將指標操作取消,而 Rust 仍然保有指標操作。Java 程式需要在虛擬機器下運行,而 Rust 程式為原生機械碼。

    vs. Python

    雖然 Rust 有著部分高階語言的特性,比起 Python 這類高階直譯語言,仍需關注更多的細節,而需花費更多時間實作程式。和 Rust 相比,Python 支援更多高階特性,使用者可以用更短的時間撰寫程式。

    然而,純 Python 實作的程式效能較差,隨著程式規模增加,這個差距會更加明顯。常見的開發模式是使用一些方式將程式加速,目前已有許多方案,像是 Cython 或 PyPy 等,Rust 可視為另一個新的作法。

    vs. Go

    Rust 和 Go 時常會拿來相比,這是因為

    • 發布時間接近
    • 同為可自動管理記憶體的編譯語言
    • 兩者分別用不同的特性改善傳統編譯語言的缺點

    Go 使用一套簡單易學的語法機制,使用者可以很快熟悉大部分 Go 的特性,將其使用在自己的專案,但是,Go 缺乏部分重要的語法特性,為了向下相容,短期內這些問題不會改變;Rust 的語法機制則較完整,但語法不穩定的問題,使得 Rust 函式庫相對不穩定。

    另外,Go 依賴垃圾回收,使得 Go 較不適合即時運算 (real-time computing) 方面的應用,而 Rust 沒有這個問題。

    撰碼風格

    每個程式語言都有自己的撰碼風格 (coding style),雖然不造撰碼風格寫程式也不一定會造成程式的錯誤,維持良好且一致的撰碼風格可以使得程式碼更容易維護。

    程式碼不僅是要傳給編譯器或直譯器執行,也是要給程式設計者看的,包括未來的自己。如果曾經接手過維護不良的程式碼,還要浪費時間去猜這些程式碼的意圖,就會知道撰寫良好的程式碼的重要性。從學習程式的早期就開始習慣良好的撰碼風格,對日後會有相當的幫助。

    如果讀者想學習 Rust 的撰碼風格,可參考這裡

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk
    標籤: INTRO, RUST