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();
}
}