rust语言浅谈

Rust是由Mozilla主导开发的通用、编译型编程语言。设计准则为“安全、并发、实用”,支持函数式、并发式、过程式以及面向对象的编程风格。
Rust语言近年来非常流行,我也是Rust编程语言的爱好者,这篇文章就简单的谈一谈Rust语言的各个方面。

安全性

语言的安全性非常重要,很多的软件bug都是由于语言安全性问题导致的。

类型安全

Rust是一种强类型,而且类型安全的编程语言。并不是所有的强类型语言都能保证类型安全,例如C/C++的void*可以随意改变类型,Java的泛型会擦除类型。

内存安全

内存管理是一大难题,除了rust之外,现代的编程语言可以分为两类。

  • 手动管理内存的语言,如C/C++。

  • 使用垃圾回收算法自动管理内存的语言,如Java、C#、Go

一些语言中具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存;

在另一些语言中,程序员必须亲自分配和释放内存。

Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。在运行时,所有权系统的任何功能都不会减慢程序。

rust的所有权规则

首先,让我们看一下所有权的规则。当我们通过举例说明时,请谨记这些规则:

  1. Rust 中的每一个值都有一个被称为其 所有者owner)的变量。
  2. 值在任一时刻有且只有一个所有者。
  3. 当所有者(变量)离开作用域,这个值将被丢弃

运行效率

标准Rust性能与标准C++性能不相上下,某些场景下效率甚至高于C++。 由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务。

其他特性

rust还有一些特性来帮助开发者提高编程效率。

从根本上来说,宏是一种为写其他代码而写代码的方式,例如我们经常使用的 println! 宏和 vec! 宏。所有的这些宏以 展开 的方式来生成比你所手写出的更多的代码。Rust 的宏不是简单的文字查找与替换,宏展开生成的代码也会进行编译检查。

高级的枚举

在其他语言中,枚举一般类似于常量来使用,而rust的枚举是可以包含其他值的,从而可以实现更加高级的功能。

1
2
3
//例如常见的Option枚举,表示可能为空的值,类似于Java的Optional<T>
let mut s = Some(66);//Option<i32>类型,表示有值
s = None;//没有值

错误处理

rust使用Result<T,E>枚举包裹一个值,如果没有错误,就可以通过匹配得到Ok(T),如果有错误,则就是Err(err)。rust建议所有的Result都必须被处理,否则编译器就会发出警告。

crates包

包这个概念是node.js弄出来的,可以很方便的进行引用,rust也有依赖包功能。

并发

在web编程领域,服务器等待io的时间通常远多于CPU计算的时间,如果采用同步流程,程序在遇到网络或者文件读写的时候,就只会原地等待读写完成。node.js是推进异步编程的重大贡献者,node.js基于事件循环进行程序调度,当遇到io阻塞时,就会先执行队列中其他代码,从而提高CPU的利用率。

Go语言的协程也有类似的原理,就不说了。

一般的异步调度程序会轮询队列里面的状态信息,如果发现状态改变,再调度过来执行后续逻辑。

而rust的异步模型基于Future,它是惰性的,不会不停的查询状态信息,而是提供一个回调函数。io完成后,再调用回调函数,通知Future可以来查询状态了,它才会来查询状态信息,做出调度。

rust的并发运算除了Future异步模型外,也有原生线程,给用户多种选择。不像go语言,go语言只给用户提供了协程,而没有原生的线程,所以go语言一般都用于网络编程。

有些程序涉及到大量的运算,没有io或者很少io,这种情况下用多线程计算会更快,因为Future的异步调度也要一些开销。总结如下:

  • io密集型的程序,适合使用Future

  • CPU运算密集的程序,适合使用多线程

FFI

FFI(Foreign Function Interface)是用来与其它语言交互的接口。现实中很多程序是由不同编程语言写的,必然会涉及到跨语言调用,比如 A 语言写的函数如果想在 B 语言里面调用,这时一般有两种解决方案:

  • 将函数做成一个服务,通过进程间通信(IPC)或网络协议通信(RPC, RESTful等);
  • 直接通过 FFI 调用。

后者直接将其它语言的接口内嵌到本语言中,调用效率比前者高。当前的系统编程领域大部分被 C/C++ 占领,而 Rust 定位为系统编程语言,少不了与现有的 C/C++ 代码交互,另外为了给那些”慢”脚本语言调用,Rust 必然得对 FFI 有完善的支持。