在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开场白Rust, 作为一种通用的多范式语言(general purpose multi-paradigm language),其目标定位在C和C++一直占据主导地位的系统编程领域。 这意味着Rust可以用来编写操作系统、游戏引擎和许多对性能有相当要求的应用程序; 同时,该语言具有非常优秀的语言表达能力 ,可以用来构建高性能的web应用程序、网络服务、类型安全的数据库对象关系映射器(ORM,Object Relational Mapper )库,还可以自顶向下的编译WebAssembly,在web上运行高效。
全能高手 该语言在为嵌入式平台构建安全攸关、实时应用方面也取得了不错的成绩,比如Arm中基于Cortex-M的微控制器(目前该领域由C语言主导)。 Rust在各个领域都表现不凡,目前很难再找到另一种单一语言可以与之相比。 此外,Cloudflare、Dropbox、Chuckfish、npm等知名公司已经在其高风险(high-stakes )项目的生产中使用了它。 Rust是一种静态的严格类型编程语言。静态属性意味着编译器在编译时可以拥有关于所有变量及其类型的信息,并在编译时完成大多数检查,这使得在运行时只需进行极少的类型检查。 其严格性则表现在不允许类型之间的自动转换,并且不能在后续代码中将指向整数的变量指向字符串。 正是这个属性,Rust能够对代码进行安全重构,并在编译时捕获大多数bug,而不必都等到运行时候再来报错了。 除了高性能外,用Rust编写的程序所具备的强大表现力,使之具备高级函数风格语言的大多数特性, 比如高阶函数(higher-order functions)和惰性迭代器(lazy iterators),完全可以编译成如C/ C++程序那等高效的代码。 在有关该语言的设计上,其所建立的原则是:编译时内存安全(compile-time memory safety)、零代价抽象(zero cost abstractions)和并发无忧(fearless concurrency)。 下面来具体的解释以下这三大特性。 内存安全(Compile-Time Memory Safety)Rust编译器可以在编译时跟踪程序中拥有资源的变量,并且能够在不使用垃圾收集器的情况下,完成所有相同目的的工作。 这里,资源可以是内存地址、保存值的变量、共享内存引用、文件处理程序、网络套接字或数据库连接处理程序。
这意味着在运行时,不会出现在C中时有出现的---在释放、双重释放或悬空指针之后使用指针---的问题。 在Rust中的引用类型(在其前带有&的类型)会隐式地与一个生命周期标记('foo)关联,这有时也会由程序员显式注释。 在整个生命周期中,编译器可以跟踪代码中可以安全使用引用的位置,如果不合规,则会在编译时报告错误。 实现这一点时,Rust根据引用上的生命周期标记来运行一种借用/引用检查算法(borrow/reference checking algorithm ),以确保已释放的内存地址不能再被访问。 正因如此,如有其他变量正在使用该指针,则其无法被释放。基于原理层面的阐释,将在后边的文章中深入涉及。 零代价抽象(Zero Cost Abstractions)
这个概念始于C++。 众所周知,编程完全是为了管理复杂性而存在的,而良好的抽象有助于复杂性的管理。 这里有一个用Rust和Kotlin(一种基于Java虚拟机(JVM)的语言)进行抽象的实例,会对高级代码的编写颇有助益,并且使其易于阅读和推理。 该实例会比较Kotlin的流和Rust的迭代器在操作数字列表方面的作用,并反衬出Rust所提供的零代价抽象原则之优势。 这里的抽象是为了能够使用以其他方法作为参数的方法来过滤数字,其操作思想是基于条件,而非手动循环。 这里使用Kotlin,是因为在语法形象上与Rust相似,代码相当容易理解,这里的目标是给出一个高级的解释,因此将忽略代码中的细节,因为这个实例的全部要点是理解零代价属性。
首先,看下Kotlin中的代码:
这里创建了一个数字流(第6行)并调用一个方法链(filter和map)来转换元素,以收集偶数的平方。这些方法能够采用闭包或函数(即第八行:it -> it * it )转换集合中的每个元素。 在函数式语言中,当在流/迭代器上调用这些方法时,对于每次这样的调用,都会创建一个中间对象来保存与正在执行的操作相关的任何状态或元数据。 因此,evens和evenSquares将是分配在JVM堆上的两个不同的中间对象。显然,在堆上分配东西会导致内存开销,这就是在Kotlin的使用中必须要为抽象付出的额外代价!
这里对平方和偶数值打印如下:
@后面的十六进制值是JVM上对象的不同的哈希代码,可见是不同的对象。
在Rust中,做同样的事情如下:
在第2行调用vec![]在堆上创建一个数字列表,然后调用into_iter()使其成为一个数字迭代器/流,而into_iter()方法从一个集合(这里Vec是一个有符号的32位整数列表)中创建一个封装迭代器类型:IntoIter([1,2,3,4,5,6,7,8,9,10]), 这个迭代器类型引用原始的数字列表,然后执行过滤器(filter)和映射转换(map,第3行和第4行),就像我们在Kotlin中所做的那样。第7行和第8行打印了evens和even_squares的类型,如下所示(为了简洁起见,省略了一些细节):
可见,中间对象Filter和Map是基本迭代器结构上的封装器类型(不是在堆上分配的),其本身是一个封装器,已经装着对第2行原始数字列表的引用了。 第4行和第5行上的封装器结构分别是在调用filter和map时创建的,它们之间没有任何指针进行间接寻址,也没有强加任何像Kotlin的那样的堆分配开销。 所有这些都归结为高效的汇编代码,相当于手动写循环的效果。
结语下一篇,将会介绍Rust在并发方面的优势,以及同其它编程语言的华山论剑 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论