Rust是一個由Mozilla[11]主導開發的通用、編譯型編程語言。它的設計準則爲“安全,併發,實用”,[12][13]支持函數式,併發式,過程式以及面向對象的編程風格。
Rust 語言原本是 Mozilla 員工 Graydon Hoare 的私人項目,而 Mozilla 於 2009 年開始贊助這個項目[14],並且在 2010 年首次揭露了它的存在[15]。也在同一年,它的編譯器源代碼開始由原本的 OCaml 語言轉移到用 Rust 語言,進行 bootstrapping 工作,稱做 rustc[16],並於 2011 年實際完成[17]。這個可自我編譯的編譯器在架構上採用了 LLVM 做爲它的後端。
第一個有版本號的 Rust 編譯器於 2012 年 1 月發佈。[18] Rust 1.0 是第一個穩定版本,於 2015 年 5 月 15 日發佈。[19]
Rust 是在完全開放的情況下進行開發,並且相當歡迎社區的回饋。在 1.0 穩定版之前,語言設計也因爲通過撰寫 Servo 網頁瀏覽器排版引擎和 rustc 編譯器本身,而有進一步的改善。雖然它由 Mozilla 資助,但它其實是一個共有項目,有很大部分的代碼是來自於社區的貢獻者。[20]
設計
Rust 的設計目標之一,是要使設計大型的互聯網客戶端和服務器的任務變得更容易[21]。因此更加強調安全性、存儲器配置、以及併發處理等方面的特性。在性能上,具有額外安全保證的代碼會比 C++ 慢一些,但是如果以 C++ 也手工提供保證的情況下,則兩者性能上是相似的[22]。
它的語法設計,與 C語言和 C++ 相當相似,區塊 (block) 使用大括號隔開,流程控制的關鍵字如 if
, else
, while
等等。在保持相似性的同時,Rust 也加進了新的關鍵字,如用於模式匹配 (pattern matching) 的 match
(與 switch
相似) 則是使用 C/C++ 系統編程語言的人會相對陌生的概念。儘管在語法上相似,Rust 的語義 (semantic) 和 C/C++ 非常不同。
爲了提供存儲器安全,它的設計不允許空指針和懸空指針[23] [24] 。 數據只能通過固定的初始化形態來建構,而所有這些形態都要求它們的輸入已經分析過了[25]。 Rust 有一個檢查指針生命期間和指針凍結的系統,可以用來預防在 C++ 中許多的類型錯誤,甚至是用了智能指針功能之後會發生的類型錯誤。
它的類型系統直接地模仿了 Haskell 語言的 type class 概念,並把它稱作“traits”,可以把它看成是一種 ad hoc 多態。Rust 的作法是通過在宣告類型變量 (type variable) 的時候,在上面加上限制條件。至於 Haskell 的高階類型變量 (Higher-kinded polymorphism) 則還未支持。
Rust 雖然有垃圾回收系統,但非如 Java 或 .NET 平臺的全自動垃圾回收。Rust 1.0已不再使用垃圾回收器,而是全面改用基於引用計數的智能指針來管理內存。
類型推導也是 Rust 提供的特性之一,使用 let
語法宣告的變量可以不用宣告類型,亦不需要初始值來推斷類型。但如果在稍後的程序中從未指派任何值到該變量,編譯器會發出編譯時 (compile time) 錯誤[26]。 函數可以使用泛型化參數 (generics),但是必須綁定 Trait。沒有任何方法可以使用方法或運算符,又不宣告它們的類型,每一項都必確明確定義。
Rust 的對象系統是基於三樣東西之上的,即實現 (implementation)、Trait 以及結構化數據 (如 struct)。實現的角色類似提供 Class 關鍵字的編程語言所代表的意義,並使用 impl
關鍵字。繼承和多態則通過 Trait 實現,它們使得方法 (method) 可以在實現中被定義。結構化數據用來定義字段。實現和 trait 都無法定義字段,並且只有 trait 可以提供繼承,藉以躲避 C++ 的“鑽石繼承問題”(菱型缺陷)。
歷史
2006年,Rust作爲Graydon Hoare的個人項目首次出現。
2009年,Graydon Hoare成爲Mozilla僱員[14]。
2010年,Rust首次作爲Mozilla官方項目出現[15]。同年,Rust開始從初始編譯(由OCaml寫成)轉變爲自編譯[16]。
2011年,Rust成功的完成了移植[17]。Rust的自編譯器採用LLVM作爲其編譯後端。
2012年1月20日,第一個有版本號的預覽版Rust編譯器發佈[18]。
2013年4月4日,Mozilla基金會宣佈將與三星集團合作開發瀏覽器排版引擎Servo,此引擎將由Rust來實現[27]。
2015年5月16日,Rust 1.0.0發佈[28]。
代碼示例
下面的代碼在Rust 1.3中測試通過。
Hello World
fn main() {
println!("Hello, World!");
}
階乘
下面是三個不同版本的階乘函數,分別以遞歸、循環和迭代器的方法寫成:
// 這個函數的if-else語句中展示了Rust中可選的隱式返回值,可用於寫出更像函數式編程風格的代碼
// 與C++和其他類似的語言不同,Rust中的if-else結構不是語句而是表達式,有返回值
fn recursive_factorial(n: u32) -> u32 {
if n <= 1 {
1
} else {
n * recursive_factorial(n - 1)
}
}
fn iterative_factorial(n: u32) -> u32 {
// 變量用`let`定義,`mut`關鍵字使得變量可以變化
let mut i = 1u32;
let mut result = 1u32;
while i <= n {
result *= i;
i += 1;
}
result // 顯式返回值,與上一個函數不同
}
fn iterator_factorial(n: u32) -> u32 {
// 迭代器有多種用於變換的函數
// |accum, x| 定義了一個匿名函數
// 內聯展開等優化方法會消去區間和fold,使本函數的運行效率和上一個函數相近
(1..n + 1).fold(1, |accum, x| accum * x)
}
fn main() {
println!("Recursive result: {}", recursive_factorial(10));
println!("Iterative result: {}", iterative_factorial(10));
println!("Iterator result: {}", iterator_factorial(10));
}
併發
一個簡單的Rust併發示例:
use std::thread;
// 這個函數將創建十個同時併發運行的線程
// 若要驗證這一點,可多次運行這個程序,觀察各線程輸出順序的隨機性
fn main() {
// 這個字符串是不可變的,因此可以安全地同時被多個線程訪問
let greeting = "Hello";
let mut threads = Vec::new();
// `for`循環可用於任何實現了`iterator`特性的類型
for num in 0..10 {
threads.push(thread::spawn(move || {
// `println!` 是一個可以靜態檢查格式字符串類型的宏
// Rust的宏是基於結構的(如同Scheme)而不是基於文本的(如同C)
println!("{} from thread number {}", greeting, num);
}));
}
// 收集所有線程,保證它們在程序退出前全部結束
for thread in threads {
thread.join().unwrap();
}
}