Rust 是一门设计得非常不错的语言。我在 Twitter 上经常提起它。现在发布了 1.0 Alpha 测试版,是时候开始安利了!
不好意思图贴错了,
简介
Rust is a systems programming language that runs blazingly fast, prevents almost all crashes, and eliminates data races.
这是网站上的宣传语。作为一个系统级编程语言 Rust 并没有 .NET 或者 JVM 那样厚厚的 Runtime,并且有和 C 一样底层编程的能力。
并且非常快,速度和 C++ 差距不大,并且把 JVM .NET 甩在后面。它通过编译时检查来保证整个程序的正确和安全性,所有资源都会被回收,并且没有 GC 开销。在 Rust 中,一个变量的生存期(lifetime)是类型系统的一部分,内存何时回收会在编译时自动推算出来。
对比 C++ 它有更加干净的类型系统和语法,简洁的范型,在编译时被验证的 RAII,还有模式匹配之类独特的的函数式特性,以及强大的宏系统以及现代的模块管理。这里有一篇简单的比较。
对比 Python、Ruby 等动态语言,可能没有那么灵活,但是先进技术下的静态类型语言别有一番风味,编译器会严谨地检查你的代码,杜绝一切低级错误发生。只要你心里装着:这个变量是什么类型,这个表达式是什么类型……你就会愉快地写程序,很多时候只要程序能编译出来,那么就能良好的运行了。
这一篇文章讲了语言本身的目标。和 C++ 一样,Rust 反对一个语言遵循一种教条的范式,你可以用它进行基本无副作用的函数式编程,也可以 OOP 或者过程式编程,利用宏,更多的范式也可以支持。没有多少的语法糖扰乱视线,比起一门炫酷的语言,Rust 更倾向于成为更现代化的工业级编程语言。
P.S. Rust 以前曾用过一个宣传语:“像 Haskell 一样类型安全,像 Erlang 一样并发,像 C++ 一样的性能。”。
特性概览
语言网站上的例子很好,而且能在网页上编译运行,只是用到的特性比较少。
稍微说明一下 :: 操作符用来获取名字空间中的符号,模块和结构都有自己的名字空间,比如说 模块::结构::new(),new 函数是社区惯例的构造器函数。
上面是一个简单的程序,包括很多种有意思的特性。
最先声明的 Enum 是一个简单而特殊的数据类型,它类似于 Haskell 的 data 或者 C 中的 union,表示它可能是多种类型之一。List 这个类型可能是一个范型的 Pair,也可能是代表表尾的 Nil。(Rust 错误处理就是通过 enum 类型在标准库中优雅的实现的)
Pair 是序对,也就是拥有两个元素的元组(tuple),第一个元素的类型是范型的,用来存放数据,第二个元素是一个指向下一个 List 的 box 指针,它可能又是一个 Pair,也可能 Nil。
后面在 main 函数中使用这个数据类型。首先用 let 创建了一个变量 len 作为链表的长度。let 出来的变量是不可变的,在代码中试图改变它的值会触发编译错误。
然后用 let mut 声明了一个可变的变量:list,list 用于存放当前的链表头节点。它是可变的(mutable),也就是说修改 list 的值不会让编译器报错。(前提是类型相同)
对于创建链表,首先将一个空的表尾 Nil 作为表头(list),然后利用 for i in range(0, len)
,进行了 42 次循环,每次循环都将 list 更新成一个指向新的节点的指针,并指向旧的节点。这样就能让链表不断的增长了。
然后开始遍历这个链表, loop 代码块代表一个无限的循环。循环内是一个赋值语句,等号右边的 match 说明了要对当前的 list 做模式匹配。在模式匹配中,判断当前节点是一个 Pair 还是 Nil:是 Pair 的话就输出当前值,然后返回节点指向的下一个节点;是 Nil 的话就用 break 结束整个循环。
模式匹配
模式匹配是非常强大的特性,有了模式匹配生活简直美好。没有模式匹配的人生是灰暗的,模式匹配,啊,模式匹配……
简单来说,模式匹配有点像 case 语句的超级威力加强版,下面是一些用法。
指针
box
新的节点都是在 Box::new 中创建的,Box::new 会在堆上创建一个对象并返回指向它的指针。如果将 box 指针传给了另一个函数或者放在了某个结构体中之后,当前的函数就不再拥有这个指针,如果继续试图访问这个指针将会触发编译错误;指针永远保证自己只有一个所有者。在所有者的作用域结束的时候,资源会自动释放。
举个例子就是张三把肥皂丢在地上让李四捡,之后张三就不能用这个肥皂了,因为肥皂是李四了,如果李四没把肥皂丢给别人,李四死了以后肥皂就会和他陪葬。
borrow
如果指针那么容易发生转移的话很多事都干不了,所以有一种 borrow 指针,能将对象的所有权借出。编译器会确保有借有还——举个例子是,如果张三扔给李四一个肥皂,李四用完了以后张三嗝屁了,那么肥皂何去何从?李四扔不回去得多寂寞?所以编译器会检查代码,如果张三在取回肥皂前嗝屁,程序将不会通过编译。
Rc
一个指针只允许有一个所有者不能满足所有情况,比如说想要创建一个树或图结构。标准库中有引用计数智能指针 Rc。类似 C++ 的 shared_ptr。
举个例子,浴室里的肥皂被很多人使用,假如他们一个一个得了某种传染病死掉了,肥皂的使用人数不断减少,最后为0,肥皂也就没人用了,肥皂也就扔了。
Rust 就是这样保证不出现内存泄漏和悬空指针的。不过假如有信心的话,在 unsafe 代码块中可以写出 C 一样不安全的代码。
无尽的测试
这门语言最大的黑点是一直在测试,从未稳定。语法经常大改,以前有四种特殊的指针语法,现在指针语法被砍得只剩两种了,各种特性也都会在社区广泛的讨论下不断轮来轮去,原本作为语言基本特性的绿色线程先是被移出标准库,最近直接被删除了,在发布 Alpha 的前几天,因为社区的讨论语言作出了一种大胆的修改,将 int 和 uint (unsigned int) 改名成 isize 和 usize。用来提示这两个数据类型的大小是依赖于机器的。
我觉得这语言最成熟的一点就在漫长而开放的测试期,测试期没有什么是不能改的,只要有用户发现一个坑就可以展开讨论,如果有道理的话就会改掉。只要三天两头不修改代码,旧的代码基本上不可能在最新的编译器上运行。
对于 Rust 来说,语言本身的设计水平是很高的。这种成熟得益于它的社区,作为一个非常不稳定的测试版语言来说,Rust 的社区非常活跃,Mozilla 的试验性排版引擎 Servo 以及 Rust 编译器自己就是用 Rust 写的,这是十万行+的大项目。而且很多人用它写自己的中小项目,Rust 在游戏开发备受期待,因为它有很多超越 C++ 的现代化特性并且有和 C++ 一样的性能。有游戏引擎和库绑定(Piston,SDL2),有人正在用Rust 复刻 Doom,等等等等。同时也有许多 Web 方面的库 (1 2 3),还有人用它来做平铺式窗口管理器,这些都是很小一部分,可以在这里看到一些流行的项目。
说这些就是表示,这门语言在没有兼容性包袱的同时,拥有活跃的社区和使用者,那么就可以在广泛讨论之后大胆改掉任何社区认为不正确的语法。这是一种很棒的模式。
一旦 Beta 版发布,以后的代码就会保证兼容性了——直到 2.0 版本。Rust 编译器及整个社区对版本号的统一约定是 SEMVER 2.0,根据此版本规范 Rust 2.0 是理论上打破向后兼容性的版本,但是这不在计划之内。
谁适合 Rust?
我觉得我上面写得又臭又长一串根本不能吸引人,搞不好有反效果,现在我简短地列举一些可能会喜欢 Rust 的人。
- 嫌 C++ 太杂乱的人
- 对性能有很大需求的人
- 想要尝试接触新事物的人
- 觉得 Ruby、Python 等语言有的时候太灵活的程序员
- 喜欢写宏的程序员
- ……
我感兴趣,怎么去学呀!
这两个都是很棒的官方教程,由原 Ruby 社区的某大牛维护,入门完全没问题,唯一的缺点是最近加入的高级特性并没有文档。
中文翻译不可避免的落后于官方,所以推荐英文为主,中文为辅。