@@ -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
2824cargo 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
5349let a : i32 = 0 ;
@@ -57,34 +53,31 @@ let mut b: i32 = 0;
5753b = 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
108100fn 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
124116let a : i32 = 0 ; // 语句
@@ -128,7 +120,7 @@ let c: i32 = {
128120}; // 使用{}表达式给c赋值
129121```
130122
131- 类似的,if-else结构也是表达式 ,其最终类型来自分支综合的结果,编译器会帮你做类型推导。
123+ 类似的,if-else 结构也是表达式 ,其最终类型来自分支综合的结果,编译器会帮你做类型推导。
132124
133125``` rust
134126fn 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
2172091 . 每个值都有一个所有者。
2182102 . 同一时刻,每个值只能有一个所有者。
2192113 . 当** 所有者** 离开作用域,值将被自动清理。
220212
221- 这些原则解决了C /C++中令人头疼的** double free** 问题,** 资源释放的责任被明确指派给资源所有者** ,而所有者由编译器保证唯一。
213+ 这些原则解决了 C /C++ 中令人头疼的 ** double free** 问题,** 资源释放的责任被明确指派给资源所有者** ,而所有者由编译器保证唯一。
222214
223215对于拥有所有权的变量,赋值行为实际上是** 移动** 行为,所有权从赋值号右边移动到左边,此后右边的变量** 失效** 。
224216
225- 如果我们仍想保留右边的变量,那么必须调用` clone() ` 方法使用** 克隆** 对对象进行** 深拷贝** 。此后我们将会在内存中拥有数据相同但所有权各自管理的两份资源。
217+ 如果我们仍想保留右边的变量,那么必须调用 ` clone() ` 方法使用** 克隆** 对对象进行** 深拷贝** 。此后我们将会在内存中拥有数据相同但所有权各自管理的两份资源。
226218
227219``` rust
228220fn 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);
254246println! (" {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
268260fn length (s : & String ) -> usize {
@@ -274,7 +266,7 @@ let len = length(&s); //使用&创建一个s的借用
274266println! (" {}" , & s ); // s依旧有效,依然可以建立借用
275267```
276268
277- 与变量的可变性一样,借用的可变性也用` mut ` 关键字控制。任意时刻只能存在一个可变借用或若干个不可变借用(以免存在数据竞争),且可变借用只能从可变变量创建。举个例子:
269+ 与变量的可变性一样,借用的可变性也用 ` mut ` 关键字控制。任意时刻只能存在一个可变借用或若干个不可变借用(以免存在数据竞争),且可变借用只能从可变变量创建。举个例子:
278270
279271``` rust
280272let mut s = String :: from (" hello" );
@@ -283,13 +275,13 @@ println!("{}", &s); // error! 此前已有对s的可变借用b存在,且至少
283275b . 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
295287let v = vec! [1 , 2 , 3 ];
@@ -303,9 +295,9 @@ let y = *val_ref; //&i32可以直接通过*解引用
303295let 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
311303let 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
348340for 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| {
411403assert! (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
419411let 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