Skip to content

Commit 3dfbb84

Browse files
authored
Update rust-intro.md
1 parent 06d50be commit 3dfbb84

File tree

1 file changed

+45
-53
lines changed

1 file changed

+45
-53
lines changed

project/rust-intro.md

Lines changed: 45 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,15 @@ comments: true
1010

1111
<img src="./images/toolchain.png" alt="Rust工具链" />
1212

13-
- **Linux/MacOS:**执行以下命令下载安装
13+
- **Linux/MacOS:** 执行以下命令下载安装 `curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh`
1414

15-
```sh
16-
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
17-
```
18-
19-
- **Windows:**访问网页[Install Rust](https://rust-lang.org/tools/install/)下载安装
15+
- **Windows:** 访问网页 [Install Rust](https://rust-lang.org/tools/install/) 下载安装。
2016

21-
安装完毕后执行`rustup --version`以及`cargo --version`确定安装成功。
17+
安装完毕后执行 `rustup --version` 以及 `cargo --version` 确定安装成功。
2218

23-
> Rust Analyzer支持对代码实时检查,提供自动补全、定义跳转等功能,可以在VSCode中下载rust analyzer插件进行使用
19+
> Rust Analyzer 支持对代码实时检查,提供自动补全、定义跳转等功能,可以在 VSCode 中下载 rust analyzer 插件进行使用
2420
25-
利用`cargo`创建我们的第一个项目`helloworld`
21+
利用 `cargo` 创建我们的第一个项目 `helloworld`
2622

2723
```sh
2824
cargo new helloworld
@@ -37,17 +33,17 @@ cargo run
3733

3834
<img src="./images/result.png" alt="运行结果" style="zoom: 80%;" />
3935

40-
cargo默认以`dev`模式编译,对于正式构建(例如使用`cargo build`等),可以加上`--release`参数在发布模式下编译以获得更好的性能。
36+
cargo默认以 `dev` 模式编译,对于正式构建(例如使用 `cargo build` 等),可以加上 `--release` 参数在发布模式下编译以获得更好的性能。
4137

4238
---
4339

44-
**练习1**:使用`cargo build`**release**模式下构建可执行文件并执行(可执行文件生成在`./target/release/`)。
40+
**练习1**:使用 `cargo build`**release** 模式下构建可执行文件并执行(可执行文件生成在 `./target/release/`)。
4541

4642
---
4743

4844
## 常用类型与程序结构
4945

50-
Rust使用**`mut`关键字**来规定变量/借用的可变性,声明mut变量表示我们允许后续对变量进行修改,否则这个对象只能用于读取。
46+
Rust使用**`mut`关键字**来规定变量/借用的可变性,声明 mut 变量表示我们允许后续对变量进行修改,否则这个对象只能用于读取。
5147

5248
```rust
5349
let a: i32 = 0;
@@ -57,34 +53,31 @@ let mut b: i32 = 0;
5753
b = 1;
5854
```
5955

60-
> mut更多用于修饰借用,类似读写锁互斥的思路,一个对象可以同时有多个不可变借用,但可变借用却必须独占变量。
56+
> mut 更多用于修饰借用,类似读写锁互斥的思路,一个对象可以同时有多个不可变借用,但可变借用却必须独占变量。
6157
62-
常见的数据类型有如下几类,很多时候我们不需要显示地指定数据类型,因为Rust编译器会帮我们自动推导类型,但是`mut`依旧需要标注。
58+
常见的数据类型有如下几类,很多时候我们不需要显示地指定数据类型,因为 Rust 编译器会帮我们自动推导类型,但是 `mut` 依旧需要标注。
6359

64-
- i8, i16, i32, i64, i128, isize:有符号整数,isize表示大小取决于系统
60+
- i8, i16, i32, i64, i128, isize:有符号整数,isize 表示大小取决于系统
6561

6662
- u8, ..., usize:无符号整数
6763

6864
- f32, f64:浮点数
6965

70-
- char:**utf-8字符**,实际可能占多个字节
66+
- char:**utf-8 字符**,实际可能占多个字节
7167

7268
- (T1, ... ):组,使用`.0``.1`来访问元素
73-
7469
```rust
7570
let mut tuple: (i32, i32, i32) = (1, 2, 3);
7671
tuple.0 = 4;
7772
```
7873

7974
- [T; len]:数组,使用下标访问,超出数组长度会报错(编译期/运行期)
80-
8175
```rust
8276
let mut array: [i32; 3] = [1, 2, 3];
8377
array[3] = 4; //error, out of length!
8478
```
8579

8680
- Vec<T>:变长数组,使用下标[...]访问,运行时进行边界检查。文档:[Vec in std::vec - Rust](https://doc.rust-lang.org/std/vec/struct.Vec.html)
87-
8881
```rust
8982
let mut a = Vec::new();
9083
a.push(1);
@@ -95,14 +88,13 @@ b = 1;
9588
```
9689

9790
- String:utf-8字符串。文档:[String in std::string - Rust](https://doc.rust-lang.org/std/string/struct.String.html)
98-
9991
```rust
10092
let mut a = String::from("这是一个字符串!");
10193

10294
a.push_str("这是添加到末尾的部分!");
10395
```
10496

105-
Rust中使用`fn`关键字声明函数,参数列表的声明方式和变量相同,使用`->`标记指定函数的返回值类型。
97+
Rust 中使用 `fn` 关键字声明函数,参数列表的声明方式和变量相同,使用 `->` 标记指定函数的返回值类型。
10698

10799
```rust
108100
fn echo(mut val: i32) { //不需要返回值
@@ -118,7 +110,7 @@ fn plus_v2(a: i32, b: i32) -> i32 {
118110
}
119111
```
120112

121-
Rust 支持**以表达式作为函数的结尾**,这样可以简洁地返回表达式的值。在使用这一特性时,需要明确区分两个基本概念:表达式(Expression)与语句(Statement)。**通常可以通过是否以分号结尾来区分表达式与语句。**`{}`本身就是一个表达式,其值取括号内最后一个表达式的值。而语句我们一般认为它的类型是`()`
113+
Rust 支持**以表达式作为函数的结尾**,这样可以简洁地返回表达式的值。在使用这一特性时,需要明确区分两个基本概念:表达式(Expression)与语句(Statement)。**通常可以通过是否以分号结尾来区分表达式与语句。** `{}` 本身就是一个表达式,其值取括号内最后一个表达式的值。而语句我们一般认为它的类型是 `()`
122114

123115
```rust
124116
let a: i32 = 0; //语句
@@ -128,7 +120,7 @@ let c: i32 = {
128120
}; //使用{}表达式给c赋值
129121
```
130122

131-
类似的,if-else结构也是表达式,其最终类型来自分支综合的结果,编译器会帮你做类型推导。
123+
类似的,if-else 结构也是表达式,其最终类型来自分支综合的结果,编译器会帮你做类型推导。
132124

133125
```rust
134126
fn foo(a: i32) -> i32 {
@@ -163,13 +155,13 @@ loop {
163155
}
164156
```
165157

166-
> 多重loop嵌套可以用 '... 标记快速break到外层,有兴趣可以自行查阅
158+
> 多重 loop 嵌套可以用 '... 标记快速 break 到外层,有兴趣可以自行查阅
167159
>
168-
> for循环我们后面和迭代器一起介绍
160+
> for 循环我们后面和迭代器一起介绍
169161
170162
---
171163

172-
**练习2:**编写函数`fib(n: i32)`,当`n >= 0`时返回斐波那契数列中第`n`个数,否则返回`0`例如f(-1) = 0,f(0) = 1,f(1) = 1,f(2)= 2,f(3)=3。你可以使用**循环****递归**来实现该函数。
164+
**练习2:**编写函数 `fib(n: i32)`,当 `n >= 0` 时返回斐波那契数列中第 `n` 个数,否则返回 `0`例如 f(-1) = 0,f(0) = 1,f(1) = 1,f(2)= 2,f(3)=3。你可以使用**循环****递归**来实现该函数。
173165

174166
**参考代码:**
175167

@@ -209,20 +201,20 @@ fn fib_loop(n: i32) -> i32 {
209201

210202
常见内存管理方式:
211203

212-
- 手动分配释放(C风格):容易出错
213-
- 垃圾回收GC(Java等):有性能损耗
204+
- 手动分配释放(C 风格):容易出错
205+
- 垃圾回收GC(Java 等):有性能损耗
214206

215-
Rust中所有权机制有三个规则
207+
Rust 中所有权机制有三个规则
216208

217209
1. 每个值都有一个所有者。
218210
2. 同一时刻,每个值只能有一个所有者。
219211
3.**所有者**离开作用域,值将被自动清理。
220212

221-
这些原则解决了C/C++中令人头疼的**double free**问题,**资源释放的责任被明确指派给资源所有者**,而所有者由编译器保证唯一。
213+
这些原则解决了 C/C++ 中令人头疼的 **double free** 问题,**资源释放的责任被明确指派给资源所有者**,而所有者由编译器保证唯一。
222214

223215
对于拥有所有权的变量,赋值行为实际上是**移动**行为,所有权从赋值号右边移动到左边,此后右边的变量**失效**
224216

225-
如果我们仍想保留右边的变量,那么必须调用`clone()`方法使用**克隆**对对象进行**深拷贝**。此后我们将会在内存中拥有数据相同但所有权各自管理的两份资源。
217+
如果我们仍想保留右边的变量,那么必须调用 `clone()` 方法使用**克隆**对对象进行**深拷贝**。此后我们将会在内存中拥有数据相同但所有权各自管理的两份资源。
226218

227219
```rust
228220
fn consume(s: String) {//函数要求接受一个拥有所有权的字符串对象作为参数
@@ -233,14 +225,14 @@ fn consume_and_return(s: String) -> String { //函数要求接受一个拥有所
233225
s
234226
}
235227

236-
let s1 = String::from("hello"); //这里s1是这个字符串对象的唯一所有者
237-
let s2 = s1; //s1的所有权“移动”到了s2,此后s1失效
238-
let mut s3 = s2.clone(); //对s2进行深拷贝,s2,s3均有效
228+
let s1 = String::from("hello"); //这里 s1 是这个字符串对象的唯一所有者
229+
let s2 = s1; //s1 的所有权“移动”到了 s2,此后 s1 失效
230+
let mut s3 = s2.clone(); //对 s2 进行深拷贝,s2,s3 均有效
239231

240-
consume(s2); //s2的所有权“移动”到了函数参数中,此后s2失效,且最终字符串在函数内被销毁。
241-
let s4 = consume_and_return(s3); //s3的所有权“移动”到函数中,又被返回出来“移动”到s4
232+
consume(s2); //s2 的所有权“移动”到了函数参数中,此后 s2 失效,且最终字符串在函数内被销毁。
233+
let s4 = consume_and_return(s3); //s3 的所有权“移动”到函数中,又被返回出来“移动”到 s4
242234

243-
let s5 = s2.clone(); //error! s2的所有权已经被移动走了,再使用这个变量是非法的。
235+
let s5 = s2.clone(); //error! s2 的所有权已经被移动走了,再使用这个变量是非法的。
244236
```
245237

246238
熟悉了所有权的基本理念,我们再看一个似乎与上面代码矛盾的示例:
@@ -254,15 +246,15 @@ consume(a);
254246
println!("{a}"); //correct!
255247
```
256248

257-
这段代码不会报错,因为对于像i32、bool等简单类型,他们在进行赋值操作时并不执行所有权的移动语义,而是在栈上**拷贝Copy出一个副本**。因此无论是传参还是赋值都不会影响原变量的所有权。
249+
这段代码不会报错,因为对于像 i32、bool 等简单类型,他们在进行赋值操作时并不执行所有权的移动语义,而是在栈上**拷贝 Copy 出一个副本**。因此无论是传参还是赋值都不会影响原变量的所有权。
258250

259-
因此我们更关注Vec,String,以及自定义的结构体等复杂类型实例的所有权问题,简单类型则不用考虑。
251+
因此我们更关注 Vec,String,以及自定义的结构体等复杂类型实例的所有权问题,简单类型则不用考虑。
260252

261-
> 本质上是这些类型实现了Copy trait,如果我们为自定义类型一样实现Copy trait,它一样可以在赋值时直接拷贝出副本。
253+
> 本质上是这些类型实现了 Copy trait,如果我们为自定义类型一样实现 Copy trait,它一样可以在赋值时直接拷贝出副本。
262254
263255
所有权的机制虽然很棒,但是也带来一个问题:当程序复杂起来后,如果要用一堆中间变量来转移来转移去,那会使程序很臃肿,同时造成很大的不便。
264256

265-
Rust提供严格的**借用(Borrowing)**机制作为补充。借用可以在精确控制可变性的前提下,灵活共享数据。
257+
Rust 提供严格的**借用(Borrowing)**机制作为补充。借用可以在精确控制可变性的前提下,灵活共享数据。
266258

267259
```rust
268260
fn length(s: &String) -> usize {
@@ -274,7 +266,7 @@ let len = length(&s); //使用&创建一个s的借用
274266
println!("{}", &s); //s依旧有效,依然可以建立借用
275267
```
276268

277-
与变量的可变性一样,借用的可变性也用`mut`关键字控制。任意时刻只能存在一个可变借用或若干个不可变借用(以免存在数据竞争),且可变借用只能从可变变量创建。举个例子:
269+
与变量的可变性一样,借用的可变性也用 `mut` 关键字控制。任意时刻只能存在一个可变借用或若干个不可变借用(以免存在数据竞争),且可变借用只能从可变变量创建。举个例子:
278270

279271
```rust
280272
let mut s = String::from("hello");
@@ -283,13 +275,13 @@ println!("{}", &s); // error! 此前已有对s的可变借用b存在,且至少
283275
b.push_str(" world");
284276
```
285277

286-
而如果我们调换上面两行的顺序,那么程序就可以正常编译。因为编译器检测到可变借用`b`之后不再使用,因此在任意时间都满足借用互斥。
278+
而如果我们调换上面两行的顺序,那么程序就可以正常编译。因为编译器检测到可变借用 `b` 之后不再使用,因此在任意时间都满足借用互斥。
287279

288280
用图来表示就是:
289281

290282
<img src="./images/borrowing.png" alt="借用冲突" style="zoom:50%;" />
291283

292-
我们可以使用`*`运算符解引用获得****实现了Copy trait,例如i32等原始类型),或者直接在借用上使用`.`运算符调用方法。
284+
我们可以使用 `*` 运算符解引用获得****实现了 Copy trait,例如i32等原始类型),或者直接在借用上使用`.`运算符调用方法。
293285

294286
```rust
295287
let v = vec![1, 2, 3];
@@ -303,9 +295,9 @@ let y = *val_ref; //&i32可以直接通过*解引用
303295
let length = v_ref.len(); //直接使用.访问Vec的方法
304296
```
305297

306-
事实上,Rust通过Deref trait来控制解引用的行为,无论多少重引用,例如`&&Vec`这种双重引用,只需要一次解引用就可以正常执行了。
298+
事实上,Rust 通过 Deref trait 来控制解引用的行为,无论多少重引用,例如 `&&Vec` 这种双重引用,只需要一次解引用就可以正常执行了。
307299

308-
如果我们要从引用拷贝资源,建立一个拥有所有权的对象,那一样可以调用`clone()`方法。
300+
如果我们要从引用拷贝资源,建立一个拥有所有权的对象,那一样可以调用 `clone()` 方法。
309301

310302
```rust
311303
let v = vec![1, 2, 3]
@@ -315,7 +307,7 @@ let u = v.clone(); //或调用to_owned(),效果相同,拷贝并获得一个
315307

316308
---
317309

318-
**练习3:**编写两个版本的函数`append_suffix`,版本一接受`Vec<String>`作为参数,对其中所有的字符串添加后缀"_"后返回Vec。版本二接受`Vec<String>`的借用作为参数,做一样的修改。
310+
**练习3:** 编写两个版本的函数 `append_suffix`,版本一接受 `Vec<String>` 作为参数,对其中所有的字符串添加后缀"_"后返回Vec。版本二接受 `Vec<String>` 的借用作为参数,做一样的修改。
319311

320312
**参考代码:**
321313

@@ -342,15 +334,15 @@ fn append_suffix_v2(strings: &mut Vec<String>) {
342334

343335
## 迭代器与闭包
344336

345-
Rust中`for`循环一般与迭代器搭配使用。举个例子:
337+
Rust中 `for` 循环一般与迭代器搭配使用。举个例子:
346338

347339
```rust
348340
for i in 1..10 {
349341
println!("{}", i);
350342
}
351343
```
352344

353-
这里`1..10`实际上展开后是是range(1, 10),而`for`的作用就是从迭代器中取出元素并进行遍历。有三种常用方法为我们提供迭代器,实际上这些方法的行为都由具体trait的实现决定,这里我们只介绍惯用的实现:
345+
这里 `1..10` 实际上展开后是是 `range(1, 10)`,而 `for` 的作用就是从迭代器中取出元素并进行遍历。有三种常用方法为我们提供迭代器,实际上这些方法的行为都由具体 trait 的实现决定,这里我们只介绍惯用的实现:
354346

355347
- iter:迭代每个元素的不可变引用。
356348
- iter_mut:迭代每个元素的可变引用。
@@ -411,9 +403,9 @@ let b: Vec<i32> = a.iter().map(|i| {
411403
assert!(a.len() == b.len()); //a和b元素个数相同,且a仍有效
412404
```
413405

414-
这里闭包`|i| { i + 1 }`就是map的参数,它表示对`iter()`中取出的每个`&i32`类型的元素先解引用(这里触发**Copy**然后加1并返回。因此最终`collect()`创建出了一个****的Vec对象,每个元素是原集合元素加1的结果
406+
这里闭包 `|i| { i + 1 }` 就是map的参数,它表示对 `iter()` 中取出的每个 `&i32` 类型的元素先解引用(这里触发**Copy**然后加 1 并返回。因此最终 `collect()` 创建出了一个****的 Vec 对象,每个元素是原集合元素加 1 的结果
415407

416-
`map`的后面还可以链条式地继续加迭代器适配器,这种函数式编程的表达能力正是 Rust 迭代器的强大之处。
408+
`map` 的后面还可以链条式地继续加迭代器适配器,这种函数式编程的表达能力正是 Rust 迭代器的强大之处。
417409

418410
```rust
419411
let numbers = vec![1, 2, 3, 4, 5];
@@ -427,7 +419,7 @@ let result: Vec<i32> = numbers
427419

428420
---
429421

430-
**练习4:**编写函数`analyze_score()`,接受`&Vec<(String, i32)>`作为参数,返回优秀学生(score >= 85)的平均分。
422+
**练习4:**编写函数 `analyze_score()`,接受 `&Vec<(String, i32)>` 作为参数,返回优秀学生(score >= 85)的平均分。
431423

432424
**参考代码:**
433425

@@ -441,4 +433,4 @@ fn analyze_score(scores: &Vec<(String, i32)>) -> f32 {
441433
let sum: i32 = excellent_scores.iter().sum();
442434
sum as f32 / excellent_scores.len() as f32
443435
}
444-
```
436+
```

0 commit comments

Comments
 (0)