https://zhuanlan.zhihu.com/p/90612241
今天(2019-11-07)Rust终于发布了期待已久的v1.39版本,增加了重量级的async/await关键字支持。Rust作为一个2015年才发布正式版的新星,使用人数寥寥,却能在StackOverflow发起的“最喜爱的编程语言”年度投票中连续四年蝉联第一。Rust凭什么能够击败Python等众多语言连续四年制霸?这一切的背后有着什么样的秘密?是人性的扭曲还是道德的沦丧?
冲着async/await支持,笔者最近在一个小项目中试用了Rust v1.39。虽然只是个小工程,写完后深刻体会都了Rust无与伦比的优势。
1.决不妥协 —— 兼顾性能与安全性几十年以来,C/C++一直都是操作系统内核、浏览器、NGINX、Redis等这类性能关键(Performance Critical)软件开发的首选语言。但存在的问题也非常明显:空指针、野指针、内存越界等。轻则Segment Fault崩溃,重则导致难以发现的0-day漏洞。
寄希望于开发人员不犯一丁点错误写出内存安全的软件无异于痴人说梦。因此自90年代开始,以Java为代表的一系列自动化内存管理的编程语言兴起。通过在性能方面做出适当的妥协和让步,引入GC等机制,来实现内存安全。
从此,软件开发常常面临这样的两难选择:要么为了安全性而在性能上做出妥协;要么为了极致的性能牺牲安全性。例如在使用Java开发某网络服务时,为了实现zero-copy转发,JVM管理的byte[]已经无法满足要求,需要使用不受GC管理的Direct Buffer。比如采用netty.io中引用计数管理的ByteBuf ,使用时慎之又慎,否则非常容易导致内存泄漏或者内存提前释放,特别是加上逻辑分支异常处理的情况下这些错误难免出现。从那时起,在性能与安全性的选择上似乎就变成了“鱼与熊掌不可兼得”的难题。
“鱼与熊掌不可兼得”的难题而Rust在设计之初就设定了必须同时兼顾性能和安全性的目标——既要有"底层语言"一样的性能,又要有"高层语言"的安全性(甚至在线程安全性方面比Java/C#等语言更加安全!)。这样一种决不妥协的设计哲学,让Rust从一出生开始就注定了它的不平凡。
Rust兼顾“低层”和“高层”语言的特性 1.1 内存安全性Rust没有传统的垃圾回收器。为了解决内存安全性,Rust独创了所有权(Onwership)系统与租借检查器(Borrow Checker)。
比如下面的代码
s 是一个referent,它指向内存中的一个字符串对象。编译器编译代码时候,会在referent的作用域范围结束位置自动生成代码销毁所指的内存对象。即referent拥有着此内存对象的所有权。所有权也能够从一个referent转移到另一个referent,亦即所有权转移(move)。无论怎么转移,对于内存中的某个对象在同一时间内有且仅有一个referent获得了它的所有权。听上去referent有点像C++中的auto_ptr智能指针,但Rust的referent更加细腻和强大。
b1/b2/b3看上去很像指针,在Rust中它们被称为租借(borrow)。
其中b1是可变(mutable)租借,而b2/b3 属于不可变(immutable)租借。
在同一时间内,一个referent可以有多个不可变租借或者一个可变租借,这点类似于读写锁(Readers-Writer lock);
租借的作用域不能超过其referent作用域,以此防止野指针的出现。
referent和borrow都没有空值,因此杜绝了空指针。
多个对象之间的borrow不能出现闭环(相互引用或者循环引用),极大地避免隐式内存泄漏。