Rust语言初步印象

2016-03-20

这几天抽时间看了一下Rust语言,写一下初步印象吧。可能不太客观,因为我还一行实际Rust代码都还没写呢。

这货太像C艹了,也确实做了不少改进,但是C艹已经积重难返了。做语言就是做设计。哪些该取,哪些该舍。因为每引入一个特性,都会对语言的方方面面产生很大的影响,像内存管理,泛型,错误处理,这都是关于设计上面的哲学。

我们看下Rust是怎么做取舍的。

性能绝不做妥协。

Rust的定位应该是系统级的语言,在性能上是绝不妥协的。这一点在很多细节能够看出来。

就比如内存管理策略上面,Rust是采用了现在C艹的RAII的套路,不带垃圾回收,资源生命期结束时自动调用析构来释放资源。对于内存管理的几个套路,一个是支持垃圾回收,优点是程序员没有burden,缺点当然是性能啦。一个是全部交给程序员来管,就像C语言,优点是对底层完全控制的能力,缺点当然是维护的负担。Rust的这个取舍是,即要保留性能,又要降低程序员的心智负担。缺点是引入了学习的复杂度。

让编译器累。

好像是Rob Pike说过的取舍,“让程序员累,让编译器累,让运行时累”。让运行时累性能上会有妥协,而让程序员累的新语言,没法跟C语言竞争,所以Rust选择的是让编译器累。

Rust非常强调安全性。这个就好比说,非强类型语言不需要标注变量的类型,写起来很爽,重构起来就傻逼了,因为没有编译器不会帮忙检查类型安全。Rust更是做到了极致,又引入了Ownership和Borrow的概念。就是让编译器累死,但是保证写出来的代码不会野指针悬挂引用这些问题。一方面编译器要检测的事情太多了,另一方面写代码的人受到的约束也更多了。

还有在泛型这个点上面。不支持泛型就是让程序员累的做法;通用类型然后在运行时做反射就是让运行时累的做法;选择支持泛型就是让编译器累的做法。C语言可以用void*,这个事情就需要由程序员来保证安全性,而Go的interface{}可以反射出原始类型,但是带来运行时的性能开销。Rust支持了泛型让编译器累,但也不是没有其它代价,不是性能开销,而是引入的复杂度。

Rust选择尽量让编译器做脏活累话,在不做性能妥协下让程序员尽量轻松一点。

复杂度。

Rust还是走的对C艹的改进的路子,一样的支持了丰富的feature。现在我评价一门语言时,不会因为“它没有某个feature,所以它是垃圾”。相反,会评估它引入一个feature带来的复杂度,换取了多少价值。

Trait是我欣赏的。基于继承和基于组合的编程,选择组合要灵活的多。也没有强调面向对象的东西,这正是对C艹的改进。时间拉到若干年前,好像一门语言不是“面向对象”的,它就是垃圾。呵呵,看现在这些新语言的设计,这个话题该算是有定论了。

支持泛型。这个引入的复杂度就好比多了一个维度,从二维到三维的世界。类似组合爆炸一样,引入的复杂度都是乘的而不是加的。最直观的表现就反映在语法表示上面,Rc<RefCell<Vec<T>>>这是啥玩意嘛!再跟Trait给组合起来,那语法...原谅我智商不够用了。

指针和引用的概念还不够,因为让两个变量引用到同一个堆上的对象,就会有潜在的data race。于是再弄Ownership和Borrow还有Lifetime这些,语法上又加了一个标注,看看这是啥呀,还不够复杂么:

fn x_or_y<'a, 'b>(x: &'a str, y: &'b str) -> &'a str

我个人的观点是,复杂度本身是没有任何办法掩盖掉的,正确的做法唯有把学习曲线变得平滑。

库和框架以及工具链等等相关生态我还没看,暂时不评价了。但是不得不说这些都是一个语言能积累用户的特别重要的因素。Rust可能吸引到的用户应该主要是C艹的用户了。在极少数的领域,可能有一定的价值,比如对性能较高要求必需手动管理内存。

这是一个门槛挺高的语言,我对Rust能流行持悲观态度,不妨若干年之后回头看这篇文章,验证一下。

Rust