Rust language Intro
Rust
一门非常重视开发者用户体验的语言
如果你想从其他语言迁移到Rust,必须经过一段时期的思维转换(Paradigm Shift)
1
2
3
4 命令式(imperative)编程语言转换为函数式(function)编程语言
变量的可变性(mutable)迁移到不可变性(immutable)
弱类型语言迁移到强类型语言
从手工或者自动内存管理到通过生命周期来管理内存
Rust最大的思维转换就是变量的所有权和声明周期
1. 前置知识篇
- 内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# 栈
栈自上而下增长
栈是程序运行的基础,每当一个函数被调用时,一块连续的内存就会被栈顶分配出来,这块内存被称为帧(frame)
在编译时,一切无法确定大小或者大小可以改变的数据,都无法放在栈上,只能放在堆上
存放栈上的值,它的大小在编译期就需要确定
# 堆
堆自下而上增长
堆上分配内存时,一般都会预留一些空间
动态分配大小的内存需要被分配到堆上,动态声明周期的内存也需要分配到堆上.
堆上分配出来的每一块内存需要显式地释放,这就使堆山内存有更加灵活的声明周期,可以在不同的调用栈之间共享数据
堆可以存放大小未知或者动态伸缩的数据类型
栈上存放的数据时静态的,静态大小,静态生命周期,堆上存放的数据时动态的,动态大小,动态生命周期
# GC vs ARC
1. GC在内存分配和释放上无需额外操作,而ARC添加了大量的额外代码处理引用计数,所以GC效率更高,吞吐量(throughput)更大
2. GC释放内存的时机不确定,释放时引发的STW(Stop The World)会导致代码执行的延迟latency.一般携带GC的编程语言不适合做嵌入式系统或者实时系统 - 数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16> 程序操作的对象
# 值和类型
> 编程语言的类型可以分为原生类型和组合类型
- 原生类型(primitive type) : 所有原生类型的大小都是固定的,因此他们可以被分配到栈上
- 字符
- 整数
- 浮点数
- 布尔值
- 数组array
- 元组tuple
- 指针
- 胖指针(fat poiner): 比正常指针携带更多信息的指针
- 组合类型(composite type) : 由一组原生类型和其它类型组合而成的类型
- 结构体(structure type): 多个类型组合在一起共同表达一个值的复杂数据结构
- 标签联合(tagged union): 不相交并集 - 指针和引用
1
2指针和应用都指向内存地址,只不过二者在解引用时的行为不一样
引用只能解引用到原来的数据类型 - 函数
1
2
3
4函数时代码中重复行为的抽象
方法时对象内部定义的函数
函数是编程语言的基本要素,是对完成某个功能的一组相关语句和表达式的封装
函数往往是一等公民,意味着函数可以作为参数传递,或者作为返回值返回,也可以作为符合类型中的一个组成部分 - 闭包
1
2
3闭包是一种特殊的函数,会捕获函数体内使用到的上下文中的自由变量,作为闭包成员的一部分
闭包是将函数和其环境一起存储的一种数据结构
闭包引用的上下文中的自由变量会被捕获到闭包的结构中,成为闭包类型的一部分 - 接口&&虚表
1
2
3
4
5# 接口
> 作为一个抽象层,接口将使用方和实现方隔离开来,使两者不直接有依赖关系,大大提高了复用性和扩展性
# 虚表 virtual table
> 虚表是指向一张涵盖接口所支持方法的列表 - 并发(concurreny)与并行(parallel)
1
2
3
4
5
6
7# 并发
> 并发是同时与多件事情打交道的能力,比如系统可以在任务1做到一定程度后,保存该任务的上下文,挂起并切换到任务2,然后过段时间在切换回任务1
# 并行
> 并行是同时处理多件事情的手段,任务1和任务2可以在同一个时间片下工作,无需上下文切换
拥有高并发处理能力的编程语言会在用户程序中嵌入一个M:N的调度器,把M个并行任务,合理地分配在N个CPU core上并行运行,让程序的吞吐量达到最大 - 同步和异步
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 同步
> 指一个任务开始执行后,后续的操作回阻塞,直到这个任务结束
> 同步执行保证了代码的因果关系(causality),是程序正确性的保证
# 异步
> 指一个任务开始执行后,与它没有因果关系的其他任务可以正常执行,不必等待前一个任务结束
- Promise(future/delay/deferred): 对象用来描述未来某个时刻才能获得的结果的值
- 初始状态: Promise还未运行
- 等待pending状态: Promise已经运行,但还没有结束
- 结束状态: promise成功解析出一个值,或者执行失败
- async/await
> async/await是一个语法糖(syntactic sugar)使用状态机将promise包装起来,让异步调用的使用感觉和同步调用非常类型
- async定义一个可以并发执行的任务
- await触发并发执行 - 编程泛式
1
2
3# 泛型编程
- 数据结构的泛型
- 代码的泛型化
2. 基础知识篇
- 第一个程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41➜ tree scrape_url
scrape_url
├── Cargo.lock
├── Cargo.toml -- Rust项目的配置管理文件
├── src
│ └── main.rs
└── target
├── CACHEDIR.TAG
└── debug
├── build
├── deps
│ ├── libscrape_url-f8d1878e1d7fc033.rmeta
│ └── scrape_url-f8d1878e1d7fc033.d
├── examples
└── incremental
└── scrape_url-2n1xt1o9t0ikb
├── s-g4eprmaq6h-ez2nl7-1g41whu33uaqz
│ ├── dep-graph.bin
│ ├── query-cache.bin
│ └── work-products.bin
└── s-g4eprmaq6h-ez2nl7.lock
9 directories, 10 files
scrape_url on main [?] is 📦 v0.1.0 via 🦀 v1.52.0 took 8s
➜ cargo run
Compiling scrape_url v0.1.0 (/Users/chyiyaqing/chyi/github.com/begin-rust/scrape_url)
Finished dev [unoptimized + debuginfo] target(s) in 3.13s
Running `target/debug/scrape_url`
Fetching url: https://www.rust-lang.org/
Converting html to markdown...
Converted markdown has been saved in rust.md.
> Rust使用cargo工具管理项目
> Rust整体语法偏向C/C++风格
> Rust支持面向接口编程和泛型编程
> Rust有非常丰富的数据类型和强大的标准库
> Rust有非常丰富的控制流程
> Rust默认变量不可变,可以添加mut关键字让变量具备可变性
> Rust项目的组织可以用mod来组织代码
> Rust一个项目被称为crate (cargo new <name> -- lib 创建一个库) - Rust变量的所有权和生命周期
- Rust内存管理方式
- C的手工管理
- Java的GC
- Swift的ARC
- Rust语言特性
- 函数式编程特性
- 类型系统
- 范性编程
- 错误处理
3. 进阶篇
*
4. 并发篇
- 无畏并发(Fearless Concurrency)
5. 实战篇
- HTTPie
1
名词解释
学习过程就像《中庸》里谈治学的方法:博学之、审问之、慎思之、明辨之、笃(du)行之
算法+数据结构=程序, Pascal之父,图灵奖得主Niklaus Wirth
- Paradigm Shift : 思维转换
- Stack Overflow: 栈溢出
- heap out of bounds: 堆越界
- use after free : 堆上的内存被释放,但栈上指向堆上内存的响应指针没有被清空
- Tracing GC : 追踪式垃圾回收(通过定期标记mark找出不再被引用的对象,然后将其清理sweep掉,来自动管理内存)
- Automatic Reference Counting: 自动引用计数 (编译时,为每个函数插入retain/release语句来自动维护堆上对象的引用计数,当引用计数为零的时候,release语句就释放对象)
- throughput: 吞吐量
- syntactic sugar: 语法糖
- Principle of Least Privilege: 最小权限原则
- derive macro : 派生宏