属性(Attribute)是一种通用的用于表达元数据的特性,借鉴ECMA-334(C#)的语法来实现ECMA-335中描述的Attributes。属性只能应用于Item(元素、项),例如 use 声明、模块、函数等。元素在Rust中,Item是Crate(库)的一个组成部分。
本章将介绍Rust语言中的属性(Attribute)和编译器参数(Compiler Options)。
Rust可以让我们对某些运算符进行重载,这其中大部分的重载都是对std::ops下的trait进行重载而实现的。重载加法我们现在来实现一个只支持加法的阉割版复数:use std::ops::Add;#[derive(Debug)]struct Complex { a: f64, b: f64,}impl Add for Complex { type Output = Complex; fn add(self, other: Complex) ->
上一章讲述了如何从rust中调用c库,这一章我们讲如何把rust编译成库让别的语言通过cffi调用。调用约定和mangle正如上一章讲述的,为了能让rust的函数通过ffi被调用,需要加上extern "C"对函数进行修饰。但由于rust支持重载,所以函数名会被编译器进行混淆,就像c++一样。因此当你的函数被编译完毕后,函数名会带上一串表明函数签名的字符串。
下文提到的ffi皆指cffi。Rust作为一门系统级语言,自带对ffi调用的支持。Getting Start引入libc库由于cffi的数据类型与rust不完全相同,我们需要引入libc库来表达对应ffi函数中的类型。在Cargo.toml中添加以下行:[dependencies]libc = "0.2.9"在你的rs文件中引入库:extern crate libc在以前libc库是和rust一起发布的,后来libc被移入了crates.io通过cargo安装。
FFI(Foreign Function Interface)是用来与其它语言交互的接口,在有些语言里面称为语言绑定(language bindings),Java 里面一般称为 JNI(Java Native Interface) 或 JNA(Java Native Access)。
Rust通过限制智能指针的行为保障了编译时安全,不过仍需要对指针做一些额外的操作。*const T和*mut T在Rust中被称为“裸指针”。它允许别名,允许用来写共享所有权的类型,甚至是内存安全的共享内存类型如:Rc<T>和Arc<T>
Rust的内存安全依赖于强大的类型系统和编译时检测,不过它并不能适应所有的场景。首先,所有的编程语言都需要跟外部的“不安全”接口打交道,调用外部库等,在“安全”的Rust下是无法实现的; 其次,“安全”的Rust无法高效表示复杂的数据结构,特别是数据结构内部有各种指针互相引用的时候;再次,事实上还存在着一些操作,这些操作是安全的,但不能通过编译器的验证。
本章开始讲解 Rust 中的 Unsafe 部分。
理论上并行和语言并没有什么关系,所以在理论上的并行方式,都可以尝试用Rust来实现。本小节不会详细全面地介绍具体的并行理论知识,只介绍用Rust如何来实现相关的并行模式。Rust的一大特点是,可以保证“线程安全”。而且,没有性能损失。
同步指的是线程之间的协作配合,以共同完成某个任务。在整个过程中,需要注意两个关键点:一是共享资源的访问, 二是访问资源的顺序。通过前面的介绍,我们已经知道了如何让多个线程访问共享资源,但并没介绍如何控制访问顺序,才不会出现错误。
在消息传递之外,还存在一种广为人知的并发模型,那就是共享内存。其实如果不能共享内存,消息传递也是不能在不同的线程间传递消息,也谈不上在不同的线程间等待和通知了。共享内存是这一切得以发生的基础。如果查看源码,你会发现消息传递的内部实现就是借用了共享内存机制。
稍加考虑,上一节的练习题其实是不完整的,它只是评分系统中的一环,一个评分系统是需要先把信息从数据库或文件中读取出来,然后才是评分,最后还需要把评分结果再保存到数据库或文件中去。如果一步一步串行地做这三个步骤,是完全没有问题的。那么我们是否可以用三个线程来分别做这三个步骤呢?
并发是什么?引用Rob Pike的经典描述:并发是同一时间应对多件事情的能力其实在我们身边就有很多并发的事情,比如一边上课,一边发短信;一边给小孩喂奶,一边看电视,只要你细心留意,就会发现许多类似的事。相应地,在软件的世界里,我们也会发现这样的事,比如一边写博客,一边听音乐;一边看网页,一边下载软件等等。显而易见这样会节约不少时间,干更多的事。
本章讲解 Rust 中,并发,并行,多线程编程的相关知识。
std::marker 模块中,有两个 trait:Send 和 Sync,它们与多线程安全相关。标记为 marker trait 的 trait,它实际就是一种约定,没有方法的定义,也没有关联元素(associated items)。仅仅是一种约定,实现了它的类型必须满足这种约定。一种类型是否加上这种约定,要么是编译器的行为,要么是人工手动的行为。
直译为奶牛!开玩笑。Cow 是一个枚举类型,通过 use std::borrow::Cow; 引入。它的定义是 Clone-on-write,即写时克隆。本质上是一个智能指针。它有两个可选值:Borrowed,用于包裹对象的引用(通用引用);Owned,用于包裹对象的所有者;Cow 提供对此对象的不可变访问(比如可直接调用此对象原有的不可变方法);
Deref 是 deref 操作符 * 的 trait,比如 *v。一般理解,*v 操作,是 &v 的反向操作,即试图由资源的引用获取到资源的拷贝(如果资源类型实现了 Copy),或所有权(资源类型没有实现 Copy)。Rust 中,本操作符行为可以重载。这也是 Rust 操作符的基本特点。本身没有什么特别的。
Borrowuse std::borrow::Borrow;Borrow 提供了一个方法 .borrow()。对于一个类型为 T 的值 foo,如果 T 实现了 Borrow<U>,那么,foo 可执行 .borrow() 操作,即 foo.borrow()。操作的结果,我们得到了一个类型为 &U 的新引用。Borrow 可以认为是 AsRef 的严格版本,它对普适引用操作的前后类型之间附加了一些其它限制。
std::convert 下面,还有另外两个 Trait,AsRef/AsMut,它们功能是配合泛型,在执行引用操作的时候,进行自动类型转换。这能够使一些场景的代码实现得清晰漂亮,大家方便开发。AsRefAsRef 提供了一个方法 .as_ref()。对于一个类型为 T 的对象 foo,如果 T 实现了 AsRef<U>,那么,foo 可执行 .as_ref() 操作,即 foo.as_ref()。
关注时代Java