Rust v1.39发布 - 这个编程语言真不通常!

https://zhuanlan.zhihu.com/p/90612241html

今天(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不能出现闭环(相互引用或者循环引用),极大地避免隐式内存泄漏。

关于referent和borrow的详细规则可见下图(原图地址)。

全部的这些规则都由编译器实现。简而言之,Rust经过编译期的规则实现了没有GC下的内存安全。

1.2 线程安全性

在内存安全的基础上,Rust更进一步地保证了线程安全性。

  • 默认状况下一个对象只能被拥有全部权的线程访问。
  • 实现了Send trait的数据类型全部权可以在多个线程间转移,但线程间不存在共享数据。
  • 实现了Sync trait的数据类型可以被多个线程同时访问,表明着该数据类型实现了线程间的同步操做。

须要说明的是,SendSync都属于编译器自动添加的标记(marker trait),并不须要开发人员实现。若是某个数据类型全部成员都是SendSync的,那么该数据类型也会被自动标记为SendSync。这样若是代码可以编译经过就天然而然地说明线程安全。

1.3 性能

benchmark显示Rust有着媲美C/C++的效率,就执行速度而言狂虐Golang/Java。

TechEmpower进行的主流Web框架性能测试中,由微软员工Kim使用Rust构建的actix开源框架在多项测试场景中拔得头筹。

依据历年的数据,70%的安全漏洞都和内存安全有关。Rust完美解决了安全性和性能的两难问题,也让它成为了开发底层应用的更优选择。目前,Intel微软Linux都在积极准备使用Rust开发驱动程序甚至操做系统内核以改善日益严峻的安全问题。

近年来CVE中内存安全类漏洞比例图

2.兼容并包 —— 支持多种编程范式

Rust在设计之初被定位为多范式语言(multi-paradigm language),在汲取了其它语言大量成功经验基础上,并不限定用户采用某种特定的范式。

2.1 命令式风格与函数式风格

Rust支持传统的命令式风格(imperative style)代码无须赘述。Rust同时也吸收了大量ML阵营函数式风格(functional style)编程语言的诸多特性,好比模式匹配、不可变量与无反作用方法、Haskell类型系统、反向类型推导和trait特性系统等。这使Rust拥有极致的性能和对底层细腻的掌控能力的同时,具有高度抽象的表达能力。

2.2 多线程模型

使用Rust进行多线程应用开发,一样拥有极高的自由度。

  • 能够选择线程间共享数据,经过传统的操做系统原语同步或者lockless数据结构;
  • 也能够选择CSP(Communicating sequential processes)模型,就如Golang开发手册中总结的那样“Do not communicate by sharing memory; instead, share memory by communicating.”。
  • 还能够选择如Erlang那样的异步Actor模型

3 v1.39添加了async/await 异步编程支持

对于高并发I/O应用的开发,异步编程是首选项。传统的异步依赖于回调或者闭包,实现起来麻烦不说,代码可读性也差强人意。而Rust此次新添加的两个关键字相似于C#和Python中的async / await ,用户无需再依赖于回调或者闭包,用最天然的方式编写可读性佳的I/O操做代码,代码看上去和同步代码无异,而编译器将异步调用部分编译为状态机从而实现异步执行。这点感受很是好。

4.我的使用体会

Rust学习曲线略为陡峭,突破新手期所需时间数倍于Golang/Python,若是有着C++和Haskell经验对这一阶段帮助很大。突破新手期后就一片坦途,再也不像编写C/C++程序那样谨小慎微,而async/await关键字的引入大大方便了异步编程,不少时候感受都在用C#/Java这样的高层语言编程同样--关注于业务和程序结构而不用去担忧某个对象错误地释放。

须要吐槽的是各自为战的生态系统,为了熟练使用async/await关键字,关于future就须要知晓std::future::Future 和futures::future::Future, 然后者还分为0.1版本和0.3版本。又因为不一样的库依赖于不一样的future实现,因此还须要精通它们之间各类转换,开始时真有点头大╮(﹀_﹀)╭。

瑕不掩瑜。Rust能够说是目前最全面的编程语言。它既能够用于开发与硬件打交道的底层,也能用来开发对性能有极致要求的微服务,甚至还能够开发运行在浏览器中的WebAssembly网站。在拥有极致性能的同时又不失鲁棒性和安全性。编程风格自由且高效,可以被普遍的其它语言开发者(C++/Haskell/Python/Scala等)接纳。这些优点决定了Rust将会大放异彩!

最后来一段使用Seed开发的WebAssembly浏览器页面代码展现:

fn view(model: &Model) -> impl View<Msg> {
    let plural = if model.count == 1 {""} else {"s"};

    // Attrs, Style, Events, and children may be defined separately.
    let outer_style = style!{
            St::Display => "flex";
            St::FlexDirection => "column";
            St::TextAlign => "center"
    };

    div![ outer_style,
        h1![ "The Grand Total" ],
        div![
            style!{
                // Example of conditional logic in a style.
                St::Color => if model.count > 4 {"purple"} else {"gray"};
                St::Border => "2px solid #004422"; 
                St::Padding => unit!(20, px);
            },
            // We can use normal Rust code and comments in the view.
            h3![ format!("{} {}{} so far", model.count, model.what_we_count, plural) ],
            button![ simple_ev(Ev::Click, Msg::Increment), "+" ],
            button![ simple_ev(Ev::Click, Msg::Decrement), "-" ],

            // Optionally-displaying an element
            if model.count >= 10 { h2![ style!{St::Padding => px(50)}, "Nice!" ] } else { empty![] }
        ],
        success_level(model.count),  // Incorporating a separate component

        h3![ "What are we counting?" ],
        input![ attrs!{At::Value => model.what_we_count}, input_ev(Ev::Input, Msg::ChangeWWC) ]
    ]
}


#[wasm_bindgen(start)]
pub fn render() {
    seed::App::build(|_, _| Init::new(Model::default()), update, view)
        .build_and_start();
}
相关文章
相关标签/搜索