在不背离安全或抽象的情况下,拥有极高的运行效率,能预防几乎所有的错误,提供优越的底层控制和性能——这些都是Rust1.0版本做出的承诺,而这仅仅是个开始。
经过几年的迭代改进,Rust编程语言日前发布1.0版本。作为一种现代系统语言,Rust从大量语言(如C/C++)中取其精髓,去其糟粕,同时具备底层控制、高性能和强大的并发性。为了做到这一点,Rust打破了许多传统的取舍,它提供:
内存安全却没有垃圾收集
具有并发性却没有数据竞争
零开销抽象
稳定且没有停滞
没有垃圾收集
软件工程中,垃圾回收是一个强大的工具,让你从手动跟踪内存的烦恼中解脱,而专注于写出更好的代码。虽然有垃圾回收器很不错的,但是它在一些领域却不那么合适,诸如操作系统、嵌入式库和(软)实时应用,它们通常需要更大程度的控制和可预测性,而垃圾回收不能提供这些。
Rust允许开发人员完全放弃垃圾收集器,且不会面临忘记释放资源、悬挂指针、段错误的风险。所有权(ownership)和借用 (borrowing)是Rust实现这些的关键概念,这个想法在编程中无处不在,也是现代C++的一个重要组成部分。但与其他语言不同的是,Rust把 他们放在了核心地带,静态地检查和利用它们,来保证没有垃圾回收器情况下内存的安全,这是之前不能想象的东西。
关于所有权(Ownership),Rust的理念是每一个值只能有一个所有者(parent)对其完全控制。随着这些值被重新分配、放在数据结 构中或传递给函数,它们的所有权会转移,且不能再通过原来的路径访问。如果超出它的作用域(scope),所有权还未被转移,它就会被自动销毁。为了使所 有权在一定范围内运作,Rust提供了一种在值的作用域范围内临时借用(指针指向值)的方法。
所有权不仅替代了垃圾回收,对保证并发性也至关重要,甚至避免诸如迭代器失效这类型的bug。Rust还适用于内存以外的其他资源,例如当你关闭套接字或者文件时,可以将你从管理中解放出来。
并发性
如前所述,所有权也保证了你的并发程序不会陷入数据竞争(data races)的隐患之中。所有权会使之保持不牢固的内存模型,通过硬件来接近它。
用Rust开始一个并发程序很简单,通过标准库将闭包传递给函数:
Java代码
use std::thread;
fn main() {
let some_string = "from the parent";
thread::spawn(move || {
// run on a new thread
println!("printing a string {}", some_string);
});
}
许多并发程序设计语言的原则之一就是共享状态应该最小化或者甚至完全禁止,取而代之的是通过消息传递,所有权意味着Rust语言中的值默认只有一个所有者,所以通过通道将一个值发送给新的线程时保证原始线程不能访问它——静态禁止共享。
然而,消息传递仅仅是工具箱中的一个工具,共享内存可能也非常有用。类型系统确保只有线程安全的数据才可以在线程之间进行共享。例如,标准库提供 了两种类型的引用计数:Arc提供线程安全的A共享内存(默认不可变),而Rc类型则可以放弃那些需要保证线程安全的同步,在Arc上提供一个性能提升。 这种类型的系统静态地确保不会不小心从一个线程向另一个线程发送Rc值。
当你想确实想改变内存时,所有权提供了进一步的帮助。标准库Mutex类型为数据提供了一个类型参量,可以受到锁的保护。之后所有权确保这个数据只有在持有锁的时候才能访问,但你不能刻意提前开锁。这种访问控制贯穿了Rust类型系统,并被广泛的用于它的标准库。
零成本抽象性能和可预测性是Rust的目标之一,达到这个目标很重要的一步就是不但要保证安全,还要提供比C++更强大的零成本抽象。Rust允许你构建高层次抽象,编译为特定代码的范型库,避免为各种情况编写代码。
为了做到这点,Rust精确控制内存布局,数据可以直接放置于堆栈或内联在其他数据结构之间,而堆分配要比大多数托管语言少,有助于获得更好的缓存局部性,而这是现代硬件中提升性能到重要因素。
这个简单的、直接的数据布局意味着优化器可以可靠地消除函数调用和类型的层,将高层代码编译为高效和可预测的机器码。迭代器是其中一个主要的例子,下面的代码是对一个32位整数序列平方求和的惯用方法:
Java代码
fn sum_squares(nums: &[i32]) -> i32 {
nums.iter()
.map(|&x| x * x)
.fold(0, |a, b| a + b)
}