update dir for pitcure

This commit is contained in:
anonymousGiga
2023-05-28 16:53:25 +08:00
parent ca6696e7ae
commit e6ab7fdf58
24 changed files with 60 additions and 60 deletions

View File

@@ -157,7 +157,7 @@ cargo new hello_world
创建后,可以看到整个目录的结构如下:
![目录结构](../assets/62.png)
![目录结构](./assets/62.png)
其中Cargo.toml是项目的配置文件src为源代码目录main.rs为主程序文件。
@@ -184,7 +184,7 @@ cargo run
运行结果如下:
![运行结果](../assets/63.png)
![运行结果](./assets/63.png)
### 2.3.4 理解Rust源码的基本结构

View File

@@ -116,7 +116,7 @@ fn main() {
在上面的代码中vehicle1和vehicle1都是Vehicle trait对象的引用对于vehicle1来说trait对象的具体类型是`Car`对于vehicle2来说trait对象的具体类型是`Truck`。上面代码对应的内存布局如下:
![注释](../../assets/14.png)
![注释](.././assets/14.png)
变量car和变量truck分别是Car类型和Truck类型存储在栈上vehicle1和vehicle2是Vehicle trait对象的引用具有两个指针其中指针ptr指向具体类型的实例vptr指向虚函数表虚函数表存储在只读数据区。

View File

@@ -21,7 +21,7 @@ fn main() {
上面代码中x的有效作用域是从第4行到第6行花括号结束前即从第6行的花括号后开始x变为无效r为x的应用此时将指向的是一块无效的内存。其内存示意图如下
![注释](../../assets/15.png)
![注释](.././assets/15.png)
## 3.11.2 借用检查

View File

@@ -15,11 +15,11 @@ fn main() {
运行该程序会打印如下错误:
![注释](../../assets/16.png)
![注释](.././assets/16.png)
运行时添加```RUST_BACKTRACE=1```,可以打印完整的堆栈,上面的代码运行时加```RUST_BACKTRACE=1```的结果如下:
![注释](../../assets/17.png)
![注释](.././assets/17.png)
## 3.12.2 用Result处理可恢复错误

View File

@@ -2,13 +2,13 @@
指针是一个包含了内存地址的变量,该内存地址引用或者指向了另外的数据,其在内存中的示意图如下:
![注释](../../assets/18.png)
![注释](.././assets/18.png)
智能指针是一类数据结构,其表现类似于指针,但是相对于指针来说,还拥有额外的元数据。最明显的是它们拥有一个引用计数。引用计数记录了智能指针总共有多少个所有者,并且当没有任何所有者时清除数据。普通引用和智能指针的另一个**非常重要的区别**就是:**引用只是只借用数据的指针,而智能指针则是拥有它们指向的数据**。
**智能指针是一个胖指针,但是胖指针不一定是智能指针**。前面章节介绍过的```String```类型就是一个智能指针,而它对应的切片引用```&str```则只仅仅是一个胖指针,区别就在于```String```类型拥有对数据的所有权,而```&str```没有。两者在内存中的示意图如下:
![注释](../../assets/19.png)
![注释](.././assets/19.png)
同样的,**Vec类型也是一个智能指针**。

View File

@@ -16,7 +16,7 @@ fn main() {
前面示例中使用```Box```定义了变量```b```,其内存布局方式如下:
![注释](../../assets/20.png)
![注释](.././assets/20.png)
## 3. Box适合使用的场景
@@ -45,7 +45,7 @@ fn main() {
```
但是上面的代码无法编译通过,因为```Cons```类型在编译时无法确定其具体大小。其内存示意图如下:
![注释](../../assets/22.png)
![注释](.././assets/22.png)
此时就需要使用Box其代码如下
```Rust
@@ -65,7 +65,7 @@ fn main() {
```
使用Box后上面的Cons的内存表示如下
![注释](../../assets/23.png)
![注释](.././assets/23.png)
每个Box的大小是固定的所以编译不会有问题。

View File

@@ -24,7 +24,7 @@ fn main() {
运行该代码,会有如下结果:
![注释](../../assets/24.png)
![注释](.././assets/24.png)
在上面示例代码中并没有打印语句但是在drop方法中实现了打印可以看到当_a和_b离开作用域时自动调用了drop方法。
@@ -53,6 +53,6 @@ fn main() {
代码运行结果如下:
![注释](../../assets/25.png)
![注释](.././assets/25.png)
第一个“Dog leave”打印是第14行调用释放_a产生最后一个“Dog leave”打印则是_b离开作用域时调用drop方法产生。

View File

@@ -4,7 +4,7 @@
假定有这样一个需求,希望创建两个共享第三个列表所有权的列表,其概念类似于如下图:
![注释](../../assets/26.png)
![注释](.././assets/26.png)
根据前面的知识,可能写出来的代码如下:
@@ -39,7 +39,7 @@ fn main() {
上面代码中a、b、c就共享数据5。当创建b时不会获取a的所有权会克隆a所包含的Rc(5u32)这会使引用计数从1增加到2并允许a和b共享Rc(5u32)的所有权。
创建c时也会克隆 a引用计数从2增加为3。每次调用`Rc::clone`Rc(5u32)的引用计数都会增加,直到有零个引用之前其数据都不会被清理。其在内存中的表示如下:
![注释](../../assets/27.png)
![注释](.././assets/27.png)
前面共享列表的需求则可以使用Rc实现如下
```Rust
@@ -58,7 +58,7 @@ fn main() {
对应的内存示意图如下:
![注释](../../assets/28.png)
![注释](.././assets/28.png)

View File

@@ -3,7 +3,7 @@
## 1. 引用循环和内存泄露
下图是一个循环链表:
![注释](../../assets/29.png)
![注释](.././assets/29.png)
下面的代码试图用之前学到的智能指针相关知识实现上面的链表:
@@ -55,23 +55,23 @@ fn main() {
- 当执行完第26行后内存布局如下
![注释](../../assets/30.png)
![注释](.././assets/30.png)
- 在执行完第33行后b对应的Rc引用计数变成2内存布局如下
![注释](../../assets/31.png)
![注释](.././assets/31.png)
此时如果第40行代码执行将会panic因为已经成了一个循环链表Rust无法匹配到a的tail最终会造成栈溢出。
- 在最后离开作用域时Rust将会对b和a调用drop方法对b调用drop方法后内存布局如下
![注释](../../assets/32.png)
![注释](.././assets/32.png)
此时b的Rc实例引用计数减去1但是仍然不为0因为第33行让a也引用了b的Rc实例所以b所指向的内存并不会被释放。
- 然后Rust尝试drop a其对应的Rc示例引用计数减去1但仍然不为0所以调用a的drop后内存布局为
![注释](../../assets/33.png)
![注释](.././assets/33.png)
至此,造成内存泄露。
@@ -162,7 +162,7 @@ fn main() {
下图为上面代码的内存布局示意图:
![注释](../../assets/34.png)
![注释](.././assets/34.png)
下面再总结一下Weak的特点

View File

@@ -18,11 +18,11 @@ cargo new main
进入main文件夹其目录层级如下
![注释](../../assets/35.png)
![注释](.././assets/35.png)
其中Cargo.toml的内容如下
![注释](../../assets/36.png)
![注释](.././assets/36.png)
可以看到这个crate的名字为main。
@@ -50,6 +50,6 @@ members = [
最后的目录层级关系如下:
![注释](../../assets/37.png)
![注释](.././assets/37.png)
上面的步骤就创建了一个包my-pack这个包包含两个crate分别是my-lib和main。

View File

@@ -160,4 +160,4 @@ fn main() {
整个工程的目录结构如下:
![注释](../../assets/38.png)
![注释](.././assets/38.png)

View File

@@ -28,7 +28,7 @@ cd main
打开main目录下的Cargo.toml文件在[dependencies]下添加对rand库的依赖即添加语句rand = "0.8.5"),添加后整个文件的内容如下:
![注释](../../assets/39.png)
![注释](.././assets/39.png)
### 3在代码中使用rand库
编写src/main.rs如下

View File

@@ -68,6 +68,6 @@ fn main() {
在上面的示例中通过my-project/Cargo.toml文件管理了main和add两个crate上面所有步骤完成后整个项目构成如下
![注释](../../assets/40.png)
![注释](.././assets/40.png)
在my-project目录下或者my-project/main目录下运行cargo run可以编译执行整个项目。

View File

@@ -41,7 +41,7 @@ cargo test
```
运行后可以看到对应的测试结果:
![注释](../../assets/58.png)
![注释](.././assets/58.png)
## 2. 使用断言
在前面的示例中测试代码中使用了assert_eq!断言。除了assert_eq!以外,下面几个也是比较常用的断言:

View File

@@ -73,7 +73,7 @@ cargo test add_two_and_two
执行结果如下图:
![注释](../../assets/59.png)
![注释](.././assets/59.png)
## 4. 过滤运行测试
@@ -84,7 +84,7 @@ cargo test add
执行结果如下图:
![注释](../../assets/60.png)
![注释](.././assets/60.png)
## 5. 忽略某个测试
有时候运行cargo test时想忽略其中的某个测试此时可以通过使用ignore属性来标记该测试来排除它。例如有如下测试代码
@@ -116,4 +116,4 @@ mod tests {
```
上面的代码中将add_two_and_two用#[ignore]忽略运行cargo test将不会执行该函数执行结果如下
![注释](../../assets/61.png)
![注释](.././assets/61.png)

View File

@@ -13,7 +13,7 @@ pub mod sub;
运行```cargo doc --open```出现如下界面:
![注释](../../assets/41.png)
![注释](.././assets/41.png)
而下面的示例因为没有将所有的包注释放在文件最上面,所以会报错:
```Rust
@@ -74,13 +74,13 @@ pub fn add2(left: u32, right: u32) -> Option<u32> {
运行cargo test可以看到运行了用例add::add2如下
![注释](../../assets/44.png)
![注释](.././assets/44.png)
运行cargo doc --open后打开add和add2的文档分别如下
![注释](../../assets/46.png)
![注释](.././assets/46.png)
![注释](../../assets/45.png)
![注释](.././assets/45.png)
可以看到add2对应的测试用例的内容在文档中被隐藏了。
@@ -98,7 +98,7 @@ pub fn sub(left: u32, right: u32) -> Option<u32> {
```
运行cargo doc --open后出现如下
![注释](../../assets/42.png)
![注释](.././assets/42.png)
从上图中点击红色划线部分将分别跳转到标准库的Option和crate::add的位置。
@@ -112,4 +112,4 @@ pub struct A;
运行cargo doc --open后出现如下
![注释](../../assets/43.png)
![注释](.././assets/43.png)

View File

@@ -27,15 +27,15 @@ fn main() {
运行前整个项目的目录结构如下:
![注释](../../assets/49.png)
![注释](.././assets/49.png)
运行cargo run执行程序结果如下
![注释](../../assets/50.png)
![注释](.././assets/50.png)
运行后整个项目的目录结构如下:
![注释](../../assets/51.png)
![注释](.././assets/51.png)
- 构建脚本的生命周期
@@ -84,7 +84,7 @@ cc = "1.0.46" # 可以在build.rs中使用cc相关的功能
下面的示例展示如何在Rust中使用c代码整个项目的目录结构如下
![注释](../../assets/52.png)
![注释](.././assets/52.png)
c目录中的pass.c为c代码源码如下
```Rust

View File

@@ -8,7 +8,7 @@
下面示例展示在c中调用Rust其目录结构如下
![注释](../../assets/53.png)
![注释](.././assets/53.png)
目录中的foo是Rust代码用cargo new foo --lib创建其中src/lib.rs的代码如下
@@ -53,4 +53,4 @@ int main()
在main.c同级目录运行命令```gcc -o main main.c ./foo/target/debug/libfoo.a -lpthread -ldl```会生成执行文件main如下图
![注释](../../assets/54.png)
![注释](.././assets/54.png)

View File

@@ -24,7 +24,7 @@ struct A {
下面给出一个完整的自定义derive宏的示例整个项目的目录如下
![注释](../../assets/55.png)
![注释](.././assets/55.png)
整个项目中有三个crate功能分别如下
@@ -138,7 +138,7 @@ fn main() {
## 2. 类函数宏
类函数宏是类似于函数那样的过程宏,下面的工程演示类函数宏,其目录结构如下:
![注释](../../assets/56.png)
![注释](.././assets/56.png)
### 1定义工作空间
编写最外层的Cargo.toml文件内容如下
@@ -195,7 +195,7 @@ fn main() {
## 3. 类属性宏
属性宏和自定义derive宏类似不同的是derive宏生成代码而类属性宏可以创建新的属性。自定义derive宏只能用于结构体和枚举属性宏则还可以用于其它的项如函数。下面的工程演示类属性宏其目录结构如下
![注释](../../assets/57.png)
![注释](.././assets/57.png)
### 1定义工作空间
编写最外层的Cargo.toml文件内容如下

View File

@@ -99,4 +99,4 @@ cargo doc --open
将打开上面代码里面文档注释生成的文档,如下图:
![注释](../assets/1.png)
![注释](./assets/1.png)

View File

@@ -2,7 +2,7 @@
## 3.6.1 Rust程序内存布局
![注释](../../assets/2.png)
![注释](.././assets/2.png)
上图是一张linux系统上Rust程序的内存布局图。在linux操作系统中会划分固定的区域给内核使用即上图中的内核空间应用程序使用的是用户空间。
@@ -44,7 +44,7 @@ fn main() {
对于上面的代码在执行第17行和第18行的栈帧示意图如下
![注释](../../assets/3.png)
![注释](.././assets/3.png)
这里需要注意的是两个帧对应同样的内存地址这是因为在调用完f1函数后其对应的栈帧释放释放的实际意义就是这段内存可以被重新分配了然后调用f2函数为其分配栈帧时从同样的地址进行分配。
@@ -64,4 +64,4 @@ Rust没有GC但通过其独特的机制管理内存程序员不用手动
当数据需要存放到堆上时,内存分配器则是根据数据的大小,在堆内存找到合适大小的空区域存放,把它标记为已使用,并返回一个表示该位置地址的指针。该指针存储在栈上,当需要访问具体的数据时,必须先访问指针,然后通过指针找到堆上的位置,从而访问数据。这个过程可以用下图表示:
![注释](../../assets/4.png)
![注释](.././assets/4.png)

View File

@@ -158,7 +158,7 @@ fn main() {
在第2行定义String类型时并不能确定最终字符串的大小所以字符串内容本身应该存储在堆上。结合什么String类型的本质的内容可以得到String类型的存储如下
![注释](../../assets/5.png)
![注释](.././assets/5.png)
String类型本身是三个字段指针、长度、容量在编译时是已知的大小存储在栈上String类型绑定的字符串在上面代码中是“AB”在编译时大小未知是运行时在堆上分配内存分配后的内存地址保存在String类型的指针字段中内存大小保存在cap字段中内存上存储的字符串长度保存在len字段中。
@@ -193,11 +193,11 @@ fn main() {
```
s是`String`类型,字符串`"Hello world"`是存储在堆内存上的,其内存布局如下:
![注释](../../assets/6.png)
![注释](.././assets/6.png)
当执行let s1 = s后内存布局如下
![注释](../../assets/7.png)
![注释](.././assets/7.png)
`let s1 = s`执行后就发生了所有权的转移String类型值的所有权从`s`转移到了`s1`。此时Rust认为原来的`s`不再有效。因此上面代码第4行打开编译将会出错。
@@ -208,14 +208,14 @@ s是`String`类型,字符串`"Hello world"`是存储在堆内存上的,其
只拷贝栈上的内容,就叫做浅拷贝。
对于上面的String类型执行`let s1 = s`后,只把`s``ptr``len``cap`中的值拷贝给`s1``ptr``len``cap`的值,这种就叫做浅拷贝。浅拷贝发生后,`s``ptr``s1``ptr`都指向同样的堆内存。内存布局如下:
![注释](../../assets/8.png)
![注释](.././assets/8.png)
### 5.2 深拷贝
除了拷贝栈上的内容外,还拷贝堆内存中的内容,就叫做深拷贝。
对于上面的String类型执行`let s1 = s`后,除了把`s``len``cap`中的值拷贝给`s1``len``cap`还在堆上重新分配一块内存将s的ptr指向的堆内存的内容拷贝到这块内存然后`s1``ptr`指向这块内存,这种拷贝就叫做深拷贝。深拷贝发生后,`s``ptr``s1``ptr`指向不同的堆内存,但是堆内存中存储的内容一样。深拷贝发生后的内存布局如下:
![注释](../../assets/9.png)
![注释](.././assets/9.png)
显然,**Rust中变量赋值Rust中叫所有权转移使用的是浅拷贝**。

View File

@@ -48,7 +48,7 @@ fn print(s: String) -> String {
上面代码中,变量`a``b``c``d`的内存布局如下:
![注释](../../assets/10.png)
![注释](.././assets/10.png)
**获取变量的引用,称之为借用** 。通过借用,允许使用被引用变量绑定的值,同时又没有移动该变量的所有权。前面的示例代码可以变成如下:
```Rust
@@ -265,11 +265,11 @@ int main()
在执行第14行前其内存布局为
![注释](../../assets/11.png)
![注释](.././assets/11.png)
当执行第14行后变成如下
![注释](../../assets/12.png)
![注释](.././assets/12.png)
第14行执行后`ptr`就变成了一个悬垂指针或者交悬垂引用然后在第16行继续使用`ptr`,则会发生错误。

View File

@@ -48,7 +48,7 @@ fn main() {
`&str``&String`的内存布局如下:
![注释](../../assets/13.png)
![注释](.././assets/13.png)
## 3. 其它Slice