update summary

This commit is contained in:
Davirain
2023-05-17 16:41:35 +08:00
parent 70101a4171
commit d3aee1ea95
74 changed files with 59 additions and 42 deletions

View File

@@ -1,4 +1,4 @@
## 3.2 基本数据类型(Basic Data Type)
## 3.2 基本数据类型
Rust是静态类型语言编译时就必须知道所有变量的类型。根据值以及其使用方式Rust编译器通常能自动推导出变量的类型。
Rust有两种数据类型子集分别是标量scalar类型和复合compound类型。
@@ -76,7 +76,7 @@ Rust中的布尔型用bool表示有两个可能的值为true和false。布
fn main() {
// 定义方式
let a: bool = true;
let b: bool = false;
let b: bool = false;
// 使用场景
if a {

View File

@@ -1,4 +1,5 @@
## 3.3 函数(Function)
## 3.3 函数
### 3.3.1. 函数定义
fn关键字、函数名、函数参数名及其类型如果有的话、返回值类型如果有的话组成函数签名 加上由一对花括号包含的函数体组成函数。例子如下:
```Rust

View File

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

View File

@@ -1,4 +1,4 @@
## 3.5 控制流(Control Flow)
## 3.5 控制流
Rust中的控制流结构主要包括

View File

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

View File

@@ -1,4 +1,5 @@
## 3.7.所有权(Ownership)
## 3.7.所有权
- [所有权介绍](./chapter_3_7_1.md)
- [引用与借用](./chapter_3_7_2.md)
- [slice类型](./chapter_3_7_3.md)

View File

@@ -26,12 +26,12 @@ fn main() {
```Rust
fn f() {
let b = 1u32; // --------------------------------|
let c = 2u32; //-----------| |
let c = 2u32; //-----------| |
// | |
// | |---b的作用域范围
println!("b = {:?}", b);// |--c的作用与范围 |
println!("c = {:?}", c);// | |
//-----------| -------------------|
//-----------| -------------------|
}
fn main() {
@@ -95,7 +95,7 @@ pub struct String {
Vec类型的定义如下
```Rust
pub struct Vec<T> {
buf: RawVec<T>,
buf: RawVec<T>,
len: usize, // 长度
}
```
@@ -118,7 +118,7 @@ struct String {
更进一步的简化可以得到String类型本质如下
```Rust
struct String {
ptrNonNull<u8>,
ptrNonNull<u8>,
cap: usize
len: usize,
}
@@ -139,7 +139,7 @@ fn main() {
```
在第2行定义String类型时并不能确定最终字符串的大小所以字符串内容本身应该存储在堆上。结合什么String类型的本质的内容可以得到String类型的存储如下
![注释](../../assets/5.png)
![注释](../assets/5.png)
String类型本身是三个字段指针、长度、容量在编译时是已知的大小存储在栈上String类型绑定的字符串在上面代码中是“AB”在编译时大小未知是运行时在堆上分配内存分配后的内存地址保存在String类型的指针字段中内存大小保存在cap字段中内存上存储的字符串长度保存在len字段中。
@@ -171,11 +171,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行打开编译将会出错。
@@ -185,14 +185,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)
- 深拷贝
除了拷贝栈上的内容外,还拷贝堆内存中的内容,就叫做深拷贝。
对于上面的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中叫所有权转移使用的是浅拷贝**。
@@ -227,11 +227,11 @@ Rust中默认实现了Copy trait的类型有
```Rust
fn main() {
let s = String::from("hello");
takes_ownership(s);
takes_ownership(s);
// println!("s: {:?}", s);//打开编译会报错因为s的所有权在上一行已经转移到take_ownership函数中了
let x = 5;
makes_copy(x);
let x = 5;
makes_copy(x);
println!("x: {:?}", x);//不会报错因为上一行将x传到makes_copy函数时会自动拷贝x的值到函数中
}
@@ -239,7 +239,7 @@ fn takes_ownership(some_string: String) {
println!("{}", some_string);
}
fn makes_copy(some_integer: i32) {
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}
```

View File

@@ -40,7 +40,7 @@ fn print(s: String) -> String {
```
上面代码中变量a、b、c、d的内存布局如下
![注释](../../assets/10.png)
![注释](../assets/10.png)
**获取变量的引用,称之为借用** 。通过借用,允许使用被引用变量绑定的值,同时又没有移动该变量的所有权。前面的示例代码可以变成如下:
```Rust
@@ -178,13 +178,13 @@ fn main() {
```
2限制二**同一作用域,可变引用和不可变引用不能同时存在**。如下代码编译错误:
```Rust
```Rust
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r3 = &mut s; // 大问题同时存在两个s的引用和一个可变引用
println!("{}, {}", r1, r2);
println!("{}, {}", r1, r2);
println!("{}", r3);
}
```
@@ -195,7 +195,7 @@ fn main() {
let r1 = &s; // 没问题
let r2 = &s; // 没问题
println!("{} and {}", r1, r2);
println!("{} and {}", r1, r2);
// 此位置之后 r1 和 r2 不再使用, 新编译器中: r1和r2离开了其作用域
let r3 = &mut s; // 没问题因为r1和r2已不存在没有同时存在对s的引用和可变引用
@@ -240,11 +240,11 @@ int main()
```
在执行第14行前其内存布局为
![注释](../../assets/11.png)
![注释](../assets/11.png)
当执行第14行后变成如下
![注释](../../assets/12.png)
![注释](../assets/12.png)
第14行执行后ptr就变成了一个悬垂指针或者交悬垂引用然后在第16行继续使用ptr则会发生错误。
@@ -264,13 +264,13 @@ fn dangle() -> &String {
*思考:为什么下面的代码是正确的*
```Rust
fn main() {
let s = no_dangle();
let s = no_dangle();
println!("s = {:?}", s);
}
fn no_dangle() -> String {
let s = String::from("hello");
s
s
} // 此处s虽然离开了函数这个作用域范围但是它的所有权是被转移出去了值并没有释放
```
@@ -279,4 +279,3 @@ fn no_dangle() -> String {
- 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
- 引用必须总是有效的(不能是悬垂引用)。

View File

@@ -18,7 +18,7 @@ fn main(){
let arr_slice1 = &arr[..=1];
println!("{:?}", arr_slice1); // [11,22];
let arr_slice2 = &mut arr[..=1];
let arr_slice2 = &mut arr[..=1];
arr_slice2[0] = 1111;
println!("{:?}", arr_slice2);// [1111,22];
println!("{:?}", arr);// [1111,22,33,44];
@@ -44,7 +44,7 @@ fn main() {
&str和&String的内存布局如下
![注释](../../assets/13.png)
![注释](../assets/13.png)
#### 3. 其它Slice
数组的Slice如下
@@ -63,4 +63,3 @@ fn main() {
println!("b: {:?}", b);
}
```