rust

rust

一 概述

1 简介

特点如下(按优秀程序从大到小排序):

  1. 能够编译成二进制:意味着

    1. 易于部署/分发

    2. 方便交叉编译

  2. 介于偏底层的系统语言(就像 C 或 C++ 语言)和基于运行时的语言(比如 Java 或 Python)之间,但更适用于系统编程领域。它是系统编程语言,系统编程语言说明它有直接操作硬件的能力,就像C/C++一样。(几乎)拥有对硬件的整体控制(内存布局,处理器功能)。相比之下Go 甚至不是系统编程语言,尽管使用它在后端基础架构上编写微服务和工具等非常棒,但我不希望使用它编写内核或内存分配器。

  3. 内存安全(memory-safe)。安全应对空指针、竞态条件和各种低级威胁。在C/C++里面,很容易会因为内存管理的问题而出现Segmentation Fault,在Rust中,由于引入了所有权(Ownership)和生存期(Lifetime)的概念,实现了自动内存管理的同时,还避免了各种内存错误。但同时,也提供unsafe块和祼指针(Raw Pointer),提供更多的自由度。可预测的运行时行为(零代价抽象 zero cost abstractions,无垃圾回收),free of charge。相比之下Go为了保证语言的简洁性和正交性,将很多底层的操作推迟到运行时来进行。

    1. 不使用 GC 使 Rust 奇快无比,特别是在需要保证延迟,而不仅仅是高吞吐量的时候

  4. 高性能高并发

    1. 具备更简单的多线程模型,类似于 C++ 或 Java。与go相比,具有更好的线程间通信能力,比如 MPSC channel(非常类似于 Go channel)

  5. 工具:

    1. Rust 的 cargo 相当出色,完善的文档,并且可以轻松扩展子命令。

  6. 高阶函数(闭包,Closure),C++11 之后也提供了Lambda来支持闭包,但在C中还没有。

  7. 模式匹配(Pattern Match)和ADT(Algebraic Data Type),在C/C++中都没有提供。模式匹配和ADT在许多函数式编程语言中都作为标准来提供了。有了ADT,可以把许多操作都统一起来,并且利用模式匹配可以很方便地把其中的数据取出来。

  8. 默认情况下不可变,这在C/C++中是完全相反。默认不可变更有利于编译器优化,而且不可变对于写并发程序有不少的好处。

  9. Trait based OO(基于特征的面向对象),和普通OO不一样。

  10. 具有强大的类型系统并支持泛型。它的设计哲学不同于 Go ,类型系统方面借鉴了 Haskell 和 C++

  11. 发布周期:更加透明;核心团队在 YouTube 上记录了所有团队会议,你可以实实在在看到他们的讨论。特别是看到语言背后的设计思考和决策很有趣。Rust 具有明确的滚动发布周期,具有良好的稳定/测试版/尝鲜版说明。

  12. 大家都说它学习曲线陡峭。开始确实比较难

    1. 独特的内存模型

    2. Rust 现在朝 Future 模型发展,其实就是我们已经熟悉的 async/await 模型,以同步的方式和思维编写代码,但以异步方式执行。

    3. Rust 还有一个功能非常强大的宏(macro)系统,可以使编译器做很多工作,比如生成代码,除此之外还有更多可以细粒度控制的细节。所以这意味着 Rust 的学习挑战很大。

  13. Rust 的低开销非常适合嵌入式编程

  14. Rust 已成为编写编译为 WebAssembly 的代码的首选语言

  15. 非面向对象,从它所有权机制的创新可以看出这一点。但是面向对象的珍贵思想(封装和继承)可以在 Rust 实现

1.4 他人评价

网友:

  1. Rust 更像是一个“实用的 Haskell (pragmatic Haskell)”,而不是“更安全的 C (safer C)”.

  2. rust更像C++

  3. 适用于对时间/空间要求苛刻的场景,比如微控制器。另一个重要场景就是 Web Assembly。在 Rust 社区中,构建编译成 Web Assembly 并跑在浏览器中工具非常多。于这一点我认为 Rust 更类似于C++。

亚马逊的 AWS 也在博客上发文表示赞助 Rust 语言,至于选择 Rust 的原因,其表示(https://amazonaws-china.com/cn/blogs/opensource/aws-sponsorship-of-the-rust-project/):

  1. 性能。Rust 非常快且内存效率高:没有运行时或垃圾收集器,它可以为关键性能服务提供支持,可以在嵌入式设备上运行,并且可以轻松地与其他语言集成;

  2. 可靠性。Rust 的丰富类型系统和所有权模型保证了内存安全性和线程安全性,并能使开发者在编译时消除许多类的错误。

  3. 生产率。Rust 拥有出色的文档,友好的编译器以及有用的错误消息以及一流的工具——集成的软件包管理器和构建工具,具有自动完成和类型检查的智能多编辑器支持,自动格式化程序等。

2 历史

Rust 编程语言核心团队有:

  1. Carol Nichols--《The Rust Programming Language》一书的合著者

Rust 是由 Mozilla 开发人员 Graydon Hoare 在 2006 年开发的个人项目,从那个时候起,该语言就像它所命名的 Rust 真菌一样,开始传播。它今天被广泛应用于构建网络、嵌入式计算机、分布式服务和命令行。

最早发布于 2014 年 9 月

2015 年,Mozilla 发布了 Rust 的首个稳定版本 v1.0。

2021年2 月 8 日,华为、微软、AWS、谷歌和 Mozilla 五大公司联合成立 Rust 基金会

3 常识

3.1 模式匹配

模式匹配:多出现在函数式编程语言之中,为其复杂的类型系统提供一个简单轻松的解构能力。比如从enum等数据结构中取出数据等等。

3.2 x..y

x..y,在rust中它是左闭右开的,即数学上的[x,y) x..=y,等价于数学上的[x,y] ..y等价于 0..y x..等价于位置 x 到数据结束 ..等价于位置 0 到结束

3.3 深拷贝和浅拷贝

rust默认全是浅拷贝,想深拷贝使用clone()

4 文档等

  1. 官方 3. https://www.rust-lang.org/ 1. start:https://www.rust-lang.org/learn 1. https://doc.rust-lang.org/book/ 1. 在线练习场:play.rust-lang.org 2. https://rustlang-cn.org/ 3. 本地查看文档rustup doc

  2. 大牛写的

    1. https://rustcc.gitbooks.io/rustprimer/content/

    2. http://www.sheshbabu.com/posts/rust-for-javascript-developers-tooling-ecosystem-overview/

6 相关项目

  1. servo

  2. deno

二 安装配置

windows

  1. 安装

    1. 使用安装文件安装

  2. 可能需要自己去系统环境变量PATH里加上安装后的bin目录,比如C:\Users\user_name\.cargo\bin

mac

  1. 安装:有三种方式

    1. brew install rust:这种安装后没有rustup,所以不推荐

    2. brew install rustup-init,然后执行rustup-init,不过这样安装,要升级 rustup就不能用rustup self update,因为brew 接管了 rustup 的更新及卸载。

    3. curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

  2. 查看是否成功安装rustc --version or cargo --version

配置

  1. 设置源

三 基础

0 架构和常见词语

组织管理

Rust 中有三个重要的组织概念:箱(Crate)、包(Package)、模块(Module)

模块

  1. 声明:使用关键字mod,模块默认都是私有的,除非带上pub

  2. 引入:rust默认将.rs文件当做一个模块,模块名就是文件名?但是普通的文件夹不能被rust编译器识别,需要在文件夹下创建mod.rs文件或者创建和文件夹同级的同名rs文件

  3. 调用

  4. 简写和别名:使用use关键字,它的作用就是简化调用的名称,比如use std::f64::consts::PI;然后就可以直接使用PI而不用加前面那么长的前缀了

访问权限

Rust 中有两种简单的访问权:公共(public)和私有(private),在没有声明pub的情况,默认都是私有的。

所有权(Ownership)

首先要明白的几点:

  1. 内存安全

    1. 其他语言可能有的问题

      1. 空指针和悬空指针

    2. 其他语言实现内存安全大多是用垃圾回收

  2. 堆栈分配:在Rust里,任何固定大小(在编译期可以知道的大小),比如机器整数(machine integers),浮点数类型,指针类型和一些其他类型会被存储在栈上。动态的和“不确定大小(unsized)”数据被存储在堆上。

所有权:

  1. "dropped"丢弃:当某些值的所有者被“释放(freed)”,或者用Rust的术语“丢弃(dropped)”,那么这个被拥有的值也会被丢弃。这些值在什么时候被丢弃?这才是吸引人的地方。当这个程序离开了变量被生命的块(block),这个变量就会被丢弃,变量的值也会被丢弃。一个块可以是一个函数,一个if语句,或者几乎是任何用大括号引入的代码块。

  2. 如何保证每一个值都被唯一的变量拥有:Rust在进行类似赋值或者给函数传值的行为时,Rust把值移动给了新的拥有者(这是一个非常重要的概念,会影响我们在Rust中写代码的方式)

  3. 如果我们真的想要有多个变量指向同一块数据该怎么实现。有两种方法

    1. 拷贝:对值进行拷贝或者克隆来处理这种情况可能是最简单但是开销最大的方式,因为最终还是要复制内存中的数据

    2. 借用(Borrowing):使用借用符&对变量进行借用。通过&的借用,不会得到值的所有权,但是能够引用该变量。

1 工具生态(Tooling Ecosystem)

  1. Version Manager:rustup -- the Rust installer and version management tool

    1. 更新rustrustup update

    2. 本地查看文档rustup doc

  2. Package:In Rust, we often refer to packages as “crates.”

    1. PackageManager:Cargo

      1. create your project with cargo new projectNameA

      2. 格式化cargo fmt

      3. 修复代码警告cargo fix

      4. build your project with cargo build

      5. run your project with cargo run

      6. test your project with cargo test

      7. build documentation for your project with cargo doc

      8. publish a library to crates.io with cargo publish

    2. Package Registry:crates.io

    3. Package Manifest:Cargo.toml

    4. Dependency Lockfile:Cargo.lock

    5. 依赖漏洞检查(Dependency Vulnerability Checker):cargo-audit

  3. compilation tool:rustc

  4. Task Runner:make, cargo-make

  5. Live Reload:cargo-watch

  6. Linter:Clippy

  7. Formatter:rustfmt

  8. 扩展

    1. rust search extension

      1. 参考

        1. https://rust.extension.sh/

      2. 安装后在地址栏输入rs就可以用这个扩展搜索了

    2. vscode扩展

      1. rust analyzer

      2. (不推荐)rust

问题

  1. cargo build

    1. Blocking waiting for file lock on package cache

      1. 删了~/.cargo/.package-cache文件

2 变量,数据类型和运算符

Rust有自动判断变量类型的能力,但它是强类型语言

使用:

  1. 类型可以使用别名,使用没区别

  2. 查看变量的类型:目前有两种方式,一种是借助编译器的错误提示,一种是借助代码

常量,不可变变量和可变变量

常量

变量分类:

  1. 按可变性分可变变量和不可变变量

  2. 按作用域分局部变量和全局变量

rust的变量分为不可变变量和可变变量:

  1. 不可变变量:Rust 语言为了高并发安全而做的设计--在语言层面尽量少的让变量的值可以改变。它不可变,但可以重影/遮蔽(Shadowing)--用同一个名字重新代表另一个变量实体,其类型、可变属性和值都可以变化。

  2. 可变变量:使用mut(英文mutable的简写)来声明,它仅仅是值可以变化。

变量的声明:使用let声明的都是局部变量,局部变量可以先声明后初始化,只要保证在使用前初始化就行了。

2.1 基础数据类型/原始数据类型(primitive type)

原始类型的值和引用都存储在栈上

字符 char

用于描述Unicode字符,大小为 4 个字节,代表 Unicode标量值

字符串

rust有三种字符串类型,且他们三个是不同的数据类型:

  1. str(&'static str)字符串字面量,它是基本数据类型,虽然它可以以字面量的方式来使用,但在实际使用中,字符串切片总是以引用的形式出现,也就是说使用的时候总是使用的&str而不是str--举个例子就是"abc"str类型,let a = "abc"中的a就是&str类型。

    1. 有点难解释它是什么:它是引用自“预分配文本(preallocated text)”的字符串切片,这个预分配文本存储在可执行程序的只读内存中(即最终生成的二进制中)。换句话说,这是装载我们程序的内存并且不依赖于在堆上分配的缓冲区。

  2. &str类型:它借用了这个文本,表示字符串切片的引用,它能够引用String类型而无需复制

    1. 它占用两个字长

      1. 指向字符串值的指针

      2. 长度

    2. 它没有容量:字符串数组的值在堆上,没有在栈上存储容量信息,它只是对字符串数组的引用,它在栈上,不管理容量,String才管理容量。

    3. 遍历

    4. 经典示例

  3. String(std::string::String)类型:变量本身存储在栈上,指向的字符串值存储在堆上。因为值在堆上,所以它可以动态增长(前提是设置为mutable)。和vec的区别是,String只能存储标准UTF-8文本

    1. 对象存储在栈上,固定的三个字长(word),分别是

      1. 实际的值指针:指向的值存储在堆上

      2. 容量

      3. 长度

Rust为什么同时具有String&str:安全性,正确性和性能。(待整理)

布尔

整型(Integer)

  1. 声明

  2. 对于溢出的处理:在debug模式下,会自动插入整数溢出检查,一旦发生溢出,引发panic。在release模式下不检查整数溢出,采用自动舍弃高位的方式

浮点数型(Floating-Point)

与其它语言一样支持 32 位浮点数f32和 64 位浮点数f64,默认是f64类型

在标准库中,有一个std::num::FpCategory枚举,表示了浮点数可能的状态:

2.2 复合数据类型

枚举 enum

初看不像其他语言那么简单

元组

元组用一对()包括的一组数据来表示,可以包含不同种类的数据

数组

数组用一对[ ]包括的同类型数据来表示

使用:

  1. 遍历

向量

向量(Vector)是一个存放多值的单数据结构,该结构将相同类型的值线性的存放在内存中。向量是线性表,在 Rust 中的表示是 Vec<T>,向量类似一个数组(array)或者列表(list),但它是动态增长的。所以向量的长度无法从逻辑上推断。

切片(slice)

rust的切片一定是引用类型,写法是&T[x..y]T是某个线性数据结构的变量名

结构体

结构体所有权

关于self的几种变体:

  1. self允许实现者移动和修改对象,对应的闭包特性为FnOnce

  2. &self既不允许实现者移动对象也不允许修改,对应的闭包特性为Fn

  3. &mut self允许实现者修改对象但不允许移动,对应的闭包特性为FnMut

  4. 不含self参数的关联函数称为静态方法 (static method)

打印结构体:结构体不能直接打印,如果想用{}占位符打印变量,需要让struct实现Display这个trait

泛型

Rust的泛型是在编译期自动推导,其他语言大多是利用反射/RTTI之类的在运行期做。(待确认)

特性

类似于其他语言中接口(interface)的概念,区别在于默认特性: 特性可以定义方法作为默认方法

变量的引用(references)

有两种方式:

  1. 不可变的&T

  2. 可变的&mut T

类型转换

类型转换有几种方式

  1. 使用内置的方法

  2. 使用关键字as

运算符

一元运算符

  1. -:取负,专门用于数值类型。实现了 std::ops::Neg

  2. * :解引用。实现了 std::ops::Derefstd::ops::DerefMut

  3. !:取反。例如!false等于true。如果这个操作符对数字类型使用,会将其每一位都置反!也就是说,对一个 1u8 进行 ! 操作,将得到一个 254u8。实现了 std::ops::Not。

  4. &&mut:租借,borrow。向一个 owner 租借其使用权,分别租借一个只读使用权和读写使用权。

算数操作符:加减乘除余

位运算符:

  1. & :与操作。实现了 std::ops::BitAnd。

  2. | :或操作。实现了 std::ops::BitOr。

  3. -^ :异或。实现了 std::ops::BitXor。

  4. << :左移运算符。实现了 std::ops::Shl。

  5. >> :右移运算符。实现了 std::ops::Shr。

逻辑运算符:

  1. 惰性 boolean 运算符:和其他类 C 语言一样会逻辑短路,但Rust 里这个运算符只能用在 bool 类型上

    1. &&

    2. ||

  2. !

比较运算符

==和!= 实现的是 PartialEq,<、>、>= 和 <=实现的是 PartialOrd

类型转换

as

重载运算符

3 流程控制(Control Flow)

没有switch:摒弃 switch 的原因是因为 switch 容易存在因忘记添加 break 而产生的串接运行问题

3.1 match

match的功能仅仅是匹配,和其他编程语言的switch类似,要想发挥出它全部的威力,需要结合"模式匹配"来使用,从而实现解构等。和switch有两点不同:

  1. match所罗列的匹配,必须穷举出其所有可能。当然,你也可以用 _ 这个符号来代表其余的所有可能性情况,就类似于switch中的default语句。

  2. match的每一个分支都必须是一个表达式,并且,除非一个分支一定会触发panic,这些分支的所有表达式的最终返回值类型必须相同。可以简单理解为整个match是一个表达式,既然是一个表达式,那么它的结果就一定是某个类型的变量。

13 错误处理

15 注释

20 并发

首先,rust本身并没有支持协程。

20.1 线程

使用:

  1. 线程的创建:被创建后,子线程将和父线程不再有关系,父线程终止不会导致子线程终止(子线程可能比父线程存活更久),除非父线程是主线程

  2. 等待线程执行结束thread.join()

20.2 管道 channel

通道(channel)可以把一个线程的消息(数据)传递到另一个线程,从而让信息在不同的线程中流动,从而实现协作。通道的两端分别是发送者(Sender)和接收者(Receiver),发送者负责从一个线程发送消息,接收者则在另一个线程中接收该消息。

分类:异步通道和同步通道

  1. 异步通道channel():不管接收者是否正在接收消息,消息发送者在发送消息时都不会阻塞。异步通道具备消息缓存的功能,理论上无上限,但是缓存大量的消息得不到即使处理,就会占用大量的内存,最终导致内存耗尽

  2. 同步通道sync_channel(msg_num)

使用:

  1. 概述

    1. 管道可以发送什么类型的消息:实现了marker trait Send的类型,这样做是为了并发安全考虑

    2. 通道如果被释放了,接收者的recv方法就会立即返回

  2. 多个发送者和多个接收者:每个发送者或接收者只能被一个线程使用,如果想被多个线程使用,使用clone()克隆发送者和接收者,底层的channel还是同一个

五 经验

Rust APIs

fmt

format!

参数可以是任意类型,而且可以 position 参数和 key-value 参数混合使用。但要注意一点,key-value 的值只能出现在 position 值之后并且不占用 position。

  1. format_arg!

  2. print宏:这两个宏只是将format!的结果输出 3. print! 4. println!

  3. write!

  4. Debug

  5. Display

println!

动词:

  1. {:?}:实现了Debug这个trait的可以用这个动词打印,比如字符串,切片,Err()

  2. {#?}打印成更漂亮的格式

thread

Rayon

for writing parallel & data race-free code.

serializing

for serializing and deserializing data.

string

Tokio/async-std

for writing non-blocking, low-latency network services.

tracing

for instrumenting Rust programs to collect structured, event-based diagnostic information.

其他

六 问题

1 warning: spurious network error (2 tries remaining)...

2 borrow of moved value

七 未整理

  1. 使用region式内存管理?

  2. 没有runtime?

Last updated

Was this helpful?