diff --git a/book/contents/basic/base-type/char-bool.md b/book/contents/basic/base-type/char-bool.md index bb9496ab..cd1a7002 100644 --- a/book/contents/basic/base-type/char-bool.md +++ b/book/contents/basic/base-type/char-bool.md @@ -4,7 +4,7 @@ ## 字符类型(char) -字符,对于没有其它编程经验的新手来说可能不太好理解(没有编程经验敢来学Rust的绝对是好汉),但是你可以把它理解为英文中的字母,中文中的汉字。 +字符,对于没有其它编程经验的新手来说可能不太好理解(没有编程经验敢来学 Rust 的绝对是好汉),但是你可以把它理解为英文中的字母,中文中的汉字。 下面的代码展示了几个颇具异域风情的字符: ``` @@ -16,9 +16,9 @@ fn main() { } ``` -如果大家是从有年代感的编程语言过来,可能会大喊一声:这XX叫字符?是的,在Rust语言中这些都是字符,Rust的字符不仅仅是`ASCII`,所有的`Unicode`值都可以作为Rust字符,包括单个的中文、日文、韩文、emoji表情符号等等,都是合法的字符类型。`Unicode` 值的范围从 `U+0000~U+D7FF`和 `U+E000~U+10FFFF`。不过“字符”并不是 `Unicode` 中的一个概念,所以人在直觉上对“字符”的理解和 Rust 的字符概念并不一致。 +如果大家是从有年代感的编程语言过来,可能会大喊一声:这XX叫字符?是的,在 Rust 语言中这些都是字符,Rust 的字符不仅仅是 `ASCII`,所有的 `Unicode` 值都可以作为 Rust 字符,包括单个的中文、日文、韩文、emoji表情符号等等,都是合法的字符类型。`Unicode` 值的范围从 `U+0000~U+D7FF` 和 `U+E000~U+10FFFF`。不过“字符”并不是 `Unicode` 中的一个概念,所以人在直觉上对“字符”的理解和 Rust 的字符概念并不一致。 -由于`Unicode`都是4个字节编码,因此字符类型也是占用4个字节: +由于 `Unicode` 都是 4 个字节编码,因此字符类型也是占用 4 个字节: ```rust fn main() { let x = '中'; @@ -35,7 +35,7 @@ $ cargo run 字符'中'占用了4字节的内存大小 ``` -> 注意,我们还没开始讲字符串,但是这里提前说一下,和一些语言不同,Rust的字符只能用`''`来表示,`""`是留给字符串的 +> 注意,我们还没开始讲字符串,但是这里提前说一下,和一些语言不同,Rust 的字符只能用 `''` 来表示, `""` 是留给字符串的 ## 布尔(bool) @@ -53,16 +53,16 @@ fn main() { } ``` -使用布尔类型的场景主要在于流程控制,例如上述代码的中的`if`就是其中之一。 +使用布尔类型的场景主要在于流程控制,例如上述代码的中的 `if` 就是其中之一。 ## 元类型 -元类型就是`()`,对,你没看错,就是`()`,唯一的值也是`()`,一些读者读到这里可能就不愿意了,你也太敷衍了吧,管这叫类型? +元类型就是 `()` ,对,你没看错,就是 `()` ,唯一的值也是 `()` ,一些读者读到这里可能就不愿意了,你也太敷衍了吧,管这叫类型? -只能说,再不起眼的东西,都有其用途,在目前为止的学习过程中,大家已经看到过很多次`fn main()`函数的使用吧?那么这个函数返回什么呢? +只能说,再不起眼的东西,都有其用途,在目前为止的学习过程中,大家已经看到过很多次 `fn main()` 函数的使用吧?那么这个函数返回什么呢? -没错,`main`函数就返回这个元类型`()`,你不能说`main`函数无返回值,因为没有返回值的函数在Rust中是有单独的定义的:`发散函数`,顾名思义,无法收敛的函数. +没错, `main` 函数就返回这个元类型 `()`,你不能说 `main` 函数无返回值,因为没有返回值的函数在 Rust 中是有单独的定义的:`发散函数`,顾名思义,无法收敛的函数。 -例如常见的`println!()`的返回值也是元类型`()`。 +例如常见的 `println!()` 的返回值也是元类型 `()`。 -再比如,你可以用`()`作为`map`的值,表示我们不关注具体的值,只关注`key`。 这种用法和Go语言的`struct{}`类似,可以作为一个值用来占位,但是完全不占用任何内存。 \ No newline at end of file +再比如,你可以用 `()` 作为 `map` 的值,表示我们不关注具体的值,只关注 `key`。 这种用法和 Go 语言的 ***struct{}*** 类似,可以作为一个值用来占位,但是完全**不占用**任何内存。 diff --git a/book/contents/basic/base-type/function.md b/book/contents/basic/base-type/function.md index 9ace8e45..4b57d5d6 100644 --- a/book/contents/basic/base-type/function.md +++ b/book/contents/basic/base-type/function.md @@ -1,8 +1,8 @@ # 函数 -Rust的函数我们在之前已经见过不少,跟其他语言几乎没有什么区别。因此本章的学习之路将轻松和愉快,骚年们,请珍惜这种愉快,下一章你将体验到不一样的Rust。 +Rust 的函数我们在之前已经见过不少,跟其他语言几乎没有什么区别。因此本章的学习之路将轻松和愉快,骚年们,请珍惜这种愉快,下一章你将体验到不一样的 Rust。 -在函数界,有一个函数只闻其名不闻其声,可以止小孩啼,在程序界只有`hello,world!`可以与之媲美,它就是`add`函数: +在函数界,有一个函数只闻其名不闻其声,可以止小孩啼!在程序界只有 `hello,world!` 可以与之媲美,它就是 `add` 函数: ```rust fn add(i: i32, j: i32) -> i32 { @@ -10,20 +10,20 @@ fn add(i: i32, j: i32) -> i32 { } ``` -该函数如此简单,但是又是如此的五脏俱全,声明函数的关键字`fn`,函数名`add()`,参数`i`和`j`,参数类型和返回值类型都是`i32`,总之一切那么的普通,但是又那么的自信,直到你看到了下面这张图: +该函数如此简单,但是又是如此的五脏俱全,声明函数的关键字 `fn` ,函数名 `add()`,参数 `i` 和 `j`,参数类型和返回值类型都是 `i32`,总之一切那么的普通,但是又那么的自信,直到你看到了下面这张图: 当你看懂了这张图,其实就等于差不多完成了函数章节的学习,但是这么短的章节显然对不起读者老爷们的厚爱,所以我们来展开下。 ## 函数要点 -- 函数名和变量名使用[蛇形命名法(snake case)](../../practice/style-guide/naming.md),例如`fn add_two() -> {}` -- 函数的位置可以随便放,Rust不关心我们在哪里定义了函数,只要有定义即可 +- 函数名和变量名使用[蛇形命名法(snake case)](../../practice/style-guide/naming.md),例如 `fn add_two() -> {}` +- 函数的位置可以随便放,Rust 不关心我们在哪里定义了函数,只要有定义即可 - 每个函数参数都需要标注类型 ## 函数参数 -Rust是强类型语言,因此需要你为每一个函数参数都标识出它的具体类型,例如: +Rust 是强类型语言,因此需要你为每一个函数参数都标识出它的具体类型,例如: ```rust fn main() { another_function(5, 6.1); @@ -35,7 +35,7 @@ fn another_function(x: i32, y: f32) { } ``` -`another_function`函数有两个参数,其中`x`是`i32`类型,`y`是`f32`类型,然后在该函数内部,打印出这两个值。这里去掉`x`或者`y`的任何一个的类型,都会报错: +`another_function` 函数有两个参数,其中 `x` 是 `i32` 类型,`y` 是 `f32` 类型,然后在该函数内部,打印出这两个值。这里去掉 `x` 或者 `y` 的任何一个的类型,都会报错: ```rust fn main() { another_function(5, 6.1); @@ -55,7 +55,8 @@ error: expected one of `:`, `@`, or `|`, found `)` 5 | fn another_function(x: i32, y) { | ^ expected one of `:`, `@`, or `|` // 期待以下符号之一 `:`, `@`, or `|` | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) // 匿名参数在Rust 2018 edition中就已经移除 + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) + // 匿名参数在 Rust 2018 edition 中就已经移除 help: if this is a parameter name, give it a type // 如果y是一个参数名,请给予它一个类型 | 5 | fn another_function(x: i32, y: TypeName) { @@ -67,9 +68,9 @@ help: if this is a type, explicitly ignore the parameter name // 如果y是一 ``` ## 函数返回 -在上一章节语句和表达式中,我们有提到在在: Rust中函数就是表达式,因此我们可以把函数的返回值直接赋给调用者。 +在上一章节语句和表达式中,我们有提到,在 Rust 中函数就是表达式,因此我们可以把函数的返回值直接赋给调用者。 -函数的返回值就是函数体最后一条表达式的返回值,当然我们也可以使用`return`提前返回,下面的函数使用最后一条表达式来返回一个值: +函数的返回值就是函数体最后一条表达式的返回值,当然我们也可以使用 `return` 提前返回,下面的函数使用最后一条表达式来返回一个值: ```rust fn plus_five(x:i32) -> i32 { x + 5 @@ -82,13 +83,13 @@ fn main() { } ``` -`x + 5`是一条表达式,求值后,返回一个值,因为它是函数的最后一行,因此该表达式的值也是函数的返回值。 +`x + 5` 是一条表达式,求值后,返回一个值,因为它是函数的最后一行,因此该表达式的值也是函数的返回值。 再来看两个重点: -1. `let x = plus_five(5)`,说明我们用一个函数的返回值来初始化`x`变量,因此侧面说明了在Rust中函数也是表达式, 这种写法等同于`let x = 5 + 5;` -2. `x + 5`没有分号,因为它是一条表达式,这个在上一节中我们也有详细介绍 +1. `let x = plus_five(5)`,说明我们用一个函数的返回值来初始化 `x` 变量,因此侧面说明了在 Rust 中函数也是表达式,这种写法等同于 `let x = 5 + 5;` +2. `x + 5` 没有分号,因为它是一条表达式,这个在上一节中我们也有详细介绍 -再来看一段代码,同时使用`return`和表达式作为返回值: +再来看一段代码,同时使用 `return` 和表达式作为返回值: ```rust fn plus_or_substract(x:i32) -> i32 { if x > 5 { @@ -105,21 +106,21 @@ fn main() { } ``` -`plus_or_substract`函数根据传入`x`的大小来决定是做加法还是减法,若`x > 5`则通过`return`提前返回`x - 5`的值,否则返回`x + 5`的值。 +`plus_or_substract` 函数根据传入 `x` 的大小来决定是做加法还是减法,若 `x > 5` 则通过 `return` 提前返回 `x - 5` 的值,否则返回 `x + 5` 的值。 #### Rust中的特殊返回类型 ##### 无返回值`()` -对于Rust新手来说,有些返回类型很难理解,而且如果你想通过百度或者谷歌去搜索,都不好查询,因为这些符号太常见了,根本难以精确搜索到。 +对于 Rust 新手来说,有些返回类型很难理解,而且如果你想通过百度或者谷歌去搜索,都不好查询,因为这些符号太常见了,根本难以精确搜索到。 -例如元类型`()`,是一个零长度的元组。它没啥作用,但是可以用来表达一个函数没有返回值: -- 函数没有返回值,那么返回一个`()` -- 通过`;`结尾的表达式返回一个`()` +例如元类型 `()`,是一个零长度的元组。它没啥作用,但是可以用来表达一个函数没有返回值: +- 函数没有返回值,那么返回一个 `()` +- 通过 `;` 结尾的表达式返回一个 `()` -例如下面的`report`函数会隐式返回一个`()`: +例如下面的 `report` 函数会隐式返回一个 `()`: ```rust use std::fmt::Debug; @@ -129,7 +130,7 @@ fn report(item: T) { } ``` -与上面的函数返回值相同,但是下面的函数显式的返回了`()`: +与上面的函数返回值相同,但是下面的函数显式的返回了 `()`: ```rust fn clear(text: &mut String) -> () { @@ -137,7 +138,7 @@ fn clear(text: &mut String) -> () { } ``` -在实际编程中,你会经常在错误提示中看到该`()`的身影出没,假如你的函数需要返回一个`u32`值,但是如果你不幸的以`表达式;`的方式作为函数的最后一行代码,就会报错: +在实际编程中,你会经常在错误提示中看到该 `()` 的身影出没,假如你的函数需要返回一个 `u32` 值,但是如果你不幸的以 `表达式;` 的方式作为函数的最后一行代码,就会报错: ```rust fn add(x:u32,y:u32) -> u32 { x + y; @@ -157,7 +158,7 @@ error[E0308]: mismatched types // 类型不匹配 | - help: consider removing this semicolon ``` -还记得我们在[语句与表达式](./statement-expression.md)中讲过的吗?只有表达式能返回值,而`;`结尾的是语句,在Rust中,一定要严格区分表达式和语句的区别,这个在其它语言中往往是被忽视的点。 +还记得我们在[语句与表达式](./statement-expression.md)中讲过的吗?只有表达式能返回值,而 `;` 结尾的是语句,在 Rust 中,一定要严格区分**表达式**和**语句**的区别,这个在其它语言中往往是被忽视的点。 ##### 永不返回的函数`!` diff --git a/book/contents/basic/base-type/statement-expression.md b/book/contents/basic/base-type/statement-expression.md index 39f70ebb..53f6bbd5 100644 --- a/book/contents/basic/base-type/statement-expression.md +++ b/book/contents/basic/base-type/statement-expression.md @@ -1,6 +1,6 @@ # 语句和表达式 -Rust的函数体是由一系列语句组成,最后由一个表达式来返回值,例如: +Rust 的函数体是由一系列语句组成,最后由一个表达式来返回值,例如: ```rust fn add_with_extra(x: i32, y: i32) -> i32 { let x = x + 1; // 语句 @@ -10,7 +10,7 @@ fn add_with_extra(x: i32, y: i32) -> i32 { ``` 语句会执行一些操作但是不会返回一个值,而表达式会在求值后返回一个值,因此在上述函数体的三行代码中,前两行是语句,最后一行是表达式。 -对于Rust语言而言,**这种基于语句和表达式的方式是非常重要的,你需要能明确的区分这两个概念**, 但是对于很多其它语言而言,这两个往往无需区分。基于表达式是函数式语言的重要特征,**表达式总要返回值**。 +对于 Rust 语言而言,**这种基于语句和表达式的方式是非常重要的,你需要能明确的区分这两个概念**, 但是对于很多其它语言而言,这两个往往无需区分。基于表达式是函数式语言的重要特征,**表达式总要返回值**。 其实,在此之前,我们已经多次使用过语句和表达式。 @@ -24,7 +24,7 @@ let (a, c) = ("hi", false); 以上都是语句,它们完成了一个具体的操作,但是并没有返回值,因此是语句。 -由于`let`是语句,因此不能将let语句赋值给其它值,如下形式是错误的: +由于 `let` 是语句,因此不能将 `let` 语句赋值给其它值,如下形式是错误的: ```rust let b = (let a = 8); @@ -32,7 +32,7 @@ let b = (let a = 8); 错误如下: ```console -error: expected expression, found statement (`let`) // 期望表达式,确发现`let`语句 +error: expected expression, found statement (`let`) // 期望表达式,却发现`let`语句 --> src/main.rs:2:13 | 2 | let b = let a = 8; @@ -40,7 +40,8 @@ error: expected expression, found statement (`let`) // 期望表达式,确发 | = note: variable declaration using `let` is a statement `let`是一条语句 -error[E0658]: `let` expressions in this position are experimental // 下面的`let`用法目前是试验性的,在稳定版中尚不能使用 +error[E0658]: `let` expressions in this position are experimental + // 下面的 `let` 用法目前是试验性的,在稳定版中尚不能使用 --> src/main.rs:2:13 | 2 | let b = let a = 8; @@ -51,13 +52,13 @@ error[E0658]: `let` expressions in this position are experimental // 下面的`l ``` -以上的错误告诉我们`let`是语句,不是表达式,因此它不返回值,也就不能给其它变量赋值。但是该错误还透漏了一个重要的信息,`let`作为表达式已经是试验功能了,也许不久的将来,我们在[`stable rust`](../../appendix/rust-version.md)下可以这样使用。 +以上的错误告诉我们 `let` 是语句,不是表达式,因此它不返回值,也就不能给其它变量赋值。但是该错误还透漏了一个重要的信息, `let` 作为表达式已经是试验功能了,也许不久的将来,我们在[`stable rust`](../../appendix/rust-version.md)下可以这样使用。 ## 表达式 -表达式会进行求值,然后返回一个值。例如`5 + 6`,在求值后,返回值`11`,因此它就是一条表达式。 +表达式会进行求值,然后返回一个值。例如 `5 + 6`,在求值后,返回值 `11`,因此它就是一条表达式。 -表达式可以成为语句的一部分,例如`let y= 6`中,`6`就是一个表达式,它在求值后返回一个值`6`(有些反直觉,但是确实是表达式). +表达式可以成为语句的一部分,例如 `let y = 6` 中, `6` 就是一个表达式,它在求值后返回一个值 `6` (有些反直觉,但是确实是表达式). 调用一个函数是表达式,因为会返回一个值,调用宏也是表达式,用花括号包裹最终返回一个值的语句块也是表达式,总之,能返回值,它就是表达式: @@ -72,7 +73,7 @@ fn main() { } ``` -上面使用一个语句块表达式将值赋给`y`变量,语句块长这样: +上面使用一个语句块表达式将值赋给 `y` 变量,语句块长这样: ```rust { let x = 3; @@ -80,5 +81,5 @@ fn main() { } ``` -该语句块是表达式的原因是:它的最后一行是表达式,返回了`x + 1`的值,注意`x + 1`不能以分号结尾,否则就会从表达式变成语句, **表达式不能包含分号**。这一点非常重要,一旦你在表达式后加上分号,它就会变成一条语句,再也不会返回一个值,请牢记! +该语句块是表达式的原因是:它的最后一行是表达式,返回了 `x + 1` 的值,注意 `x + 1` 不能以分号结尾,否则就会从表达式变成语句, **表达式不能包含分号**。这一点非常重要,一旦你在表达式后加上分号,它就会变成一条语句,再也**不会**返回一个值,请牢记! diff --git a/book/contents/basic/converse.md b/book/contents/basic/converse.md index 71f2a31e..73a01166 100644 --- a/book/contents/basic/converse.md +++ b/book/contents/basic/converse.md @@ -1,6 +1,6 @@ # 类型转换 -Rust 是类型安全的语言,因此在 Rust 中做类型转换不是一件简单的事,这一章节我们将对Rust中的类型转换进行详尽讲解。 +Rust 是类型安全的语言,因此在 Rust 中做类型转换不是一件简单的事,这一章节我们将对 Rust 中的类型转换进行详尽讲解。 ## `as`转换 先来看一段代码: @@ -17,7 +17,7 @@ fn main() { 能跟着这本书一直学习到这里,说明你对 Rust 已经有了一定的理解,那么一眼就能看出这段代码注定会报错,因为 `a` 和 `b` 拥有不同的类型,Rust 不允许两种不同的类型进行比较。 -解决办法很简单,只要把 `b` 转换成 `i32` 类型即可,Rust中内置了一些基本类型之间的转换,这里使用 `as` 操作符来完成:`if a < (b as i32) {...}`。那么为什么不把 `a` 转换成 `u16` 类型呢? +解决办法很简单,只要把 `b` 转换成 `i32` 类型即可,Rust 中内置了一些基本类型之间的转换,这里使用 `as` 操作符来完成: `if a < (b as i32) {...}`。那么为什么不把 `a` 转换成 `u16` 类型呢? 因为每个类型能表达的数据范围不同,如果把范围较大的类型转换成较小的类型,会造成错误,因此我们需要把范围较小的类型转换成较大的类型,来避免这些问题的发生。 @@ -65,7 +65,7 @@ fn main() { 就算 `e as U1 as U2` 是合法的,也不能说明 `e as U2` 是合法的(`e` 不能直接转换成 `U2`)。 ## TryInto转换 -在一些场景中,使用 `as` 关键字会有比较大的限制。如果你想要在类型转换上拥有完全的控制而不依赖内置的转换,例如处理转换错误,那么可以使用 `TryInto`: +在一些场景中,使用 `as` 关键字会有比较大的限制。如果你想要在类型转换上拥有完全的控制而不依赖内置的转换,例如处理转换错误,那么可以使用 `TryInto` : ```rust use std::convert::TryInto; @@ -122,10 +122,10 @@ fn reinterpret(foo: Foo) -> Bar { } ``` -简单粗暴,但是从另外一个角度来看,也挺啰嗦的,好在Rust为我们提供了更通用的方式来完成这个目的。 +简单粗暴,但是从另外一个角度来看,也挺啰嗦的,好在 Rust 为我们提供了更通用的方式来完成这个目的。 #### 强制类型转换 -在某些情况下,类型是可以进行隐式强制转换的,虽然这些转换弱化了 Rust 的类型系统,但是它们的存在是为了让Rust在大多数场景可以工作(说白了,帮助用户省事),而不是报各种类型上的编译错误。 +在某些情况下,类型是可以进行隐式强制转换的,虽然这些转换弱化了 Rust 的类型系统,但是它们的存在是为了让 Rust 在大多数场景可以工作(说白了,帮助用户省事),而不是报各种类型上的编译错误。 首先,在匹配特征时,不会做任何强制转换(除了方法)。一个类型 `T` 可以强制转换为 `U`,不代表 `impl T` 可以强制转换为 `impl U`,例如下面的代码就无法通过编译检查: ```rust @@ -154,7 +154,7 @@ error[E0277]: the trait bound `&mut i32: Trait` is not satisfied = note: `Trait` is implemented for `&i32`, but not for `&mut i32` ``` -`&i32`实现了特征`Trait`,`&mut i32`可以转换为`&i32`,但是`&mut i32`依然无法作为`Trait`来使用。 +`&i32` 实现了特征 `Trait`, `&mut i32` 可以转换为 `&i32`,但是 `&mut i32` 依然无法作为 `Trait` 来使用。 #### 点操作符 方法调用的点操作符看起来简单,实际上非常不简单,它在调用时,会发生很多魔法般的类型转换,例如:自动引用、自动解引用,强制类型转换直到类型能匹配等。 @@ -162,10 +162,10 @@ error[E0277]: the trait bound `&mut i32: Trait` is not satisfied 假设有一个方法 `foo`,它有一个接收器(接收器就是 `self`、`&self`、`&mut self` 参数)。如果调用 `value.foo()`,编译器在调用 `foo` 之前,需要决定到底使用哪个 `Self` 类型来调用。现在假设 `value` 拥有类型 `T`。 再进一步,我们使用[完全限定语法](https://course.rs/basic/trait/advance-trait.html#完全限定语法)来进行准确的函数调用: -1. 首先,编译器检查它是否可以直接调用`T::foo(value)`,称之为**值方法调用** -2. 如果上一步调用无法完成(例如方法类型错误或者特征没有针对 `Self` 进行实现,上文提到过特征不能进行强制转换),那么编译器会尝试增加自动引用,以为着编译器会尝试以下调用:`<&T>::foo(value)`和`<&mut T>::foo(value)`,称之为**引用方法调用** -3. 若上面两个方法依然不工作,编译器会试着解引用`T`,然后再进行尝试。这里使用了`Deref`特征 - 若`T: Deref`(`T`可以被解引用为`U`),那么编译器会使用`U`类型进行尝试,称之为**解引用方法调用** -4. 若`T`不能被解引用,且`T`是一个定长类型(在编译器类型长度是已知的),那么编译器也会尝试将`T`从定长类型转为不定长类型,例如将`[i32; 2]`转为`[i32]` +1. 首先,编译器检查它是否可以直接调用 `T::foo(value)`,称之为**值方法调用** +2. 如果上一步调用无法完成(例如方法类型错误或者特征没有针对 `Self` 进行实现,上文提到过特征不能进行强制转换),那么编译器会尝试增加自动引用,以为着编译器会尝试以下调用: `<&T>::foo(value)` 和 `<&mut T>::foo(value)`,称之为**引用方法调用** +3. 若上面两个方法依然不工作,编译器会试着解引用 `T` ,然后再进行尝试。这里使用了 `Deref` 特征 —— 若 `T: Deref` (`T` 可以被解引用为 `U`),那么编译器会使用 `U` 类型进行尝试,称之为**解引用方法调用** +4. 若 `T` 不能被解引用,且 `T` 是一个定长类型(在编译器类型长度是已知的),那么编译器也会尝试将 `T` 从定长类型转为不定长类型,例如将 `[i32; 2]` 转为 `[i32]` 5. 若还是不行,那...没有那了,最后编译器大喊一声:汝欺我甚,不干了! 下面我们来用一个例子来解释上面的方法查找算法: @@ -174,14 +174,14 @@ let array: Rc> = ...; let first_entry = array[0]; ``` -`array`数组的底层数据隐藏在了重重封锁之后,那么编译器如何使用`array[0]`这种数组原生访问语法通过重重封锁,准确的访问到数组中的第一个元素? -1. 首先,`array[0]`只是[`Index`](https://doc.rust-lang.org/std/ops/trait.Index.html)特征的语法糖: 编译器会将`array[0]`转换为`array.index(0)`调用,当然在调用之前,编译器会先检查`array`是否实现了`Index`特征. -2. 接着,编译器检查`Rc>`是否有否实现`Index`特征,结果是否,不仅如此,`&Rc> `与`&mut Rc>`也没有实现. -3. 上面的都不能工作,编译器开始对`Rc>`进行解引用,把它转变成`Box<[T; 3]>` -4. 此时继续对`Box<[T; 3]>`进行上面的操作:`Box<[T; 3]>`,`&Box<[T; 3]>`,和`&mut Box<[T; 3]>`都没有实现`Index`特征,所以编译器开始对`Box<[T; 3]>`进行解引用,然后我们得到了`[T; 3]` -5. `[T; 3]`以及它的各种引用都没有实现`Index`索引(是不是很反直觉:D,在直觉中,数组都可以通过索引访问,实际上只有数组切片才可以!),它也不能再进行解引用,因此编译器只能祭出最后的大杀器:将定长转为不定长,因此`[T; 3]`被转换成`[T]`,也就是数组切片,它实现了`Index`特征,因此最终我们可以通过`index`方法访问到对应的元素. +`array` 数组的底层数据隐藏在了重重封锁之后,那么编译器如何使用 `array[0]` 这种数组原生访问语法通过重重封锁,准确的访问到数组中的第一个元素? +1. 首先, `array[0]` 只是[`Index`](https://doc.rust-lang.org/std/ops/trait.Index.html)特征的语法糖:编译器会将 `array[0]` 转换为 `array.index(0)` 调用,当然在调用之前,编译器会先检查 `array` 是否实现了 `Index` 特征。 +2. 接着,编译器检查 `Rc>` 是否有否实现 `Index` 特征,结果是否,不仅如此,`&Rc>` 与 `&mut Rc>` 也没有实现。 +3. 上面的都不能工作,编译器开始对 `Rc>` 进行解引用,把它转变成 `Box<[T; 3]>` +4. 此时继续对 `Box<[T; 3]>` 进行上面的操作 :`Box<[T; 3]>`, `&Box<[T; 3]>`,和 `&mut Box<[T; 3]>` 都没有实现 `Index` 特征,所以编译器开始对 `Box<[T; 3]>` 进行解引用,然后我们得到了 `[T; 3]` +5. `[T; 3]` 以及它的各种引用都没有实现 `Index` 索引(是不是很反直觉:D,在直觉中,数组都可以通过索引访问,实际上只有数组切片才可以!),它也不能再进行解引用,因此编译器只能祭出最后的大杀器:将定长转为不定长,因此 `[T; 3]` 被转换成 `[T]`,也就是数组切片,它实现了 `Index` 特征,因此最终我们可以通过 `index` 方法访问到对应的元素。 -过程看起来很复杂,但是也还好挺好理解,如果你先不能彻底理解,也不要紧,等以后对Rust理解更深了,同时需要深入理解类型转换时,再来细细品读本章。 +过程看起来很复杂,但是也还好,挺好理解,如果你现在不能彻底理解,也不要紧,等以后对 Rust 理解更深了,同时需要深入理解类型转换时,再来细细品读本章。 再来看看以下更复杂的例子: ```rust @@ -189,20 +189,20 @@ fn do_stuff(value: &T) { let cloned = value.clone(); } ``` -上面例子中`cloned`的类型时什么?首先编译器检查能不能进行**值方法调用**,`value`的类型是`&T`,同时`clone`方法的签名也是`&T`: `fn clone(&T) -> T`,因此可以进行值方法调用,再加上编译器知道了`T`实现了`Clone`,因此`cloned`的类型是`T`。 +上面例子中 `cloned` 的类型时什么?首先编译器检查能不能进行**值方法调用**, `value` 的类型是 `&T`,同时 `clone` 方法的签名也是 `&T` : `fn clone(&T) -> T`,因此可以进行值方法调用,再加上编译器知道了 `T` 实现了 `Clone`,因此 `cloned` 的类型是 `T`。 -如果`T: Clone`的特征约束被移除呢? +如果 `T: Clone` 的特征约束被移除呢? ```rust fn do_stuff(value: &T) { let cloned = value.clone(); } ``` -首先,从直觉上来说,该方法会报错,因为`T`没有实现`Clone`特征,但是真实情况是什么呢? +首先,从直觉上来说,该方法会报错,因为 `T` 没有实现 `Clone` 特征,但是真实情况是什么呢? -我们先来推导一番。 首先通过值方法调用就不再可行,因此`T`没有实现`Clone`特征,也就无法调用`T`的`clone`方法。接着编译器尝试**引用方法调用**,此时`T`变成`&T`,在这种情况下,`clone`方法的签名如下:`fn clone(&&T) -> &T`,接着我们现在对`value`进行了引用。 编译器发现`&T`实现了`Clone`类型(所有的引用类型都可以被复制,因为其实就是复制一份地址),因此可以可以推出`cloned`也是`&T`类型。 +我们先来推导一番。 首先通过值方法调用就不再可行,因为 `T` 没有实现 `Clone` 特征,也就无法调用 `T` 的 `clone` 方法。接着编译器尝试**引用方法调用**,此时 `T` 变成 `&T`,在这种情况下, `clone` 方法的签名如下: `fn clone(&&T) -> &T`,接着我们现在对 `value` 进行了引用。 编译器发现 `&T` 实现了 `Clone` 类型(所有的引用类型都可以被复制,因为其实就是复制一份地址),因此可以可以推出 `cloned` 也是 `&T` 类型。 -最终,我们复制出一份引用指针,这很合理,因为值类型`T`没有实现`Clone`,只能去复制一个指针了。 +最终,我们复制出一份引用指针,这很合理,因为值类型 `T` 没有实现 `Clone`,只能去复制一个指针了。 下面的例子也是自动引用生效的地方: ```rust @@ -215,13 +215,13 @@ fn clone_containers(foo: &Container, bar: &Container) { } ``` -推断下上面的`foo_cloned`和`bar_cloned`是什么类型?提示: 关键在`Container`的泛型参数,一个是`i32`的具体类型,一个是泛型类型,其中`i32`实现了`Clone`,但是`T`并没有. +推断下上面的 `foo_cloned` 和 `bar_cloned` 是什么类型?提示: 关键在 `Container` 的泛型参数,一个是 `i32` 的具体类型,一个是泛型类型,其中 `i32` 实现了 `Clone`,但是 `T` 并没有。 -首先要复习一下复杂类型派生`Clone`的规则:一个复杂类型能否派生`Clone`,需要它内部的所有子类型都能进行`Clone`。因此`Container(Arc)`是否实现`Clone`的关键在于`T`类型是否实现了`Clone`. +首先要复习一下复杂类型派生 `Clone` 的规则:一个复杂类型能否派生 `Clone`,需要它内部的所有子类型都能进行 `Clone`。因此 `Container(Arc)` 是否实现 `Clone` 的关键在于 `T` 类型是否实现了 `Clone` 特征。 -上面代码中,`Container`实现了`Clone`特征,因此编译器可以直接进行值方法调用,此时相当于直接调用`foo.clone`,其中`clone`的函数签名是`fn clone(&T) -> T`,由此可以看出`foo_cloned`的类型是`Container`. +上面代码中,`Container` 实现了 `Clone` 特征,因此编译器可以直接进行值方法调用,此时相当于直接调用 `foo.clone`,其中 `clone` 的函数签名是 `fn clone(&T) -> T`,由此可以看出 `foo_cloned` 的类型是 `Container`。 -然而,`bar_cloned`的类型却是`&Container`.这个不合理啊,明明我们为`Container`派生了`Clone`特征,因此它也应该是`Container`类型才对。万事皆有因,我们先来看下`derive`宏最终生成的代码大概是啥样的: +然而,`bar_cloned` 的类型却是 `&Container`,这个不合理啊,明明我们为 `Container` 派生了 `Clone` 特征,因此它也应该是 `Container` 类型才对。万事皆有因,我们先来看下 `derive` 宏最终生成的代码大概是啥样的: ```rust impl Clone for Container where T: Clone { fn clone(&self) -> Self { @@ -230,11 +230,11 @@ impl Clone for Container where T: Clone { } ``` -从上面代码可以看出,派生`Clone`能实现的[根本是`T`实现了`Clone`特征](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable):`where T: Clone`, 因此`Container`就没有实现`Clone`特征。 +从上面代码可以看出,派生 `Clone` 能实现的根本是 `T` 实现了[`Clone`特征](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable):`where T: Clone`, 因此 `Container` 就没有实现 `Clone` 特征。 -编译器接着会去尝试引用方法调用,此时`&Container`引用实现了`Clone`,最终可以得出`bar_cloned`的类型是`&Container`。 +编译器接着会去尝试引用方法调用,此时 `&Container` 引用实现了 `Clone`,最终可以得出 `bar_cloned` 的类型是 `&Container`。 -当然,也可以为`Container`手动实现`Clone`特征: +当然,也可以为 `Container` 手动实现 `Clone` 特征: ```rust impl Clone for Container { fn clone(&self) -> Self { @@ -243,9 +243,9 @@ impl Clone for Container { } ``` -此时,编译器首次尝试值方法调用即可通过,因此`bar_cloned`的类型变成`Container`. +此时,编译器首次尝试值方法调用即可通过,因此 `bar_cloned` 的类型变成 `Container`。 -这一块儿内容真的挺复杂,每一个坚持看完的读者都是真正的勇士,我也是:为了写好这块儿内容,作者足足花了4个小时! +这一块儿内容真的挺复杂,每一个坚持看完的读者都是真正的勇士,我也是:为了写好这块儿内容,作者足足花了 **4** 个小时! #### 变形记(Transmutes) @@ -253,20 +253,20 @@ impl Clone for Container { 类型系统,你让开!我要自己转换这些类型,不成功便成仁!虽然本书大多是关于安全的内容,我还是希望你能仔细考虑避免使用本章讲到的内容。这是你在 Rust 中所能做到的真真正正、彻彻底底、最最可怕的非安全行为,在这里,所有的保护机制都形同虚设。 -先让你看看深渊长什么样,开开眼,然后你再决定是否深入: `mem::transmute`将类型`T`直接转成类型`U`,唯一的要求就是,这两个类型占用同样大小的字节数!我的天,这也算限制?这简直就是无底线的转换好吧?看看会导致什么问题: -1. 首先也是最重要的,转换后创建一个任意类型的实例会造成无法想象的混乱,而且根本无法预测。不要把`3`转换成`bool`类型,就算你根本不会去使用该`bool`类型,也不要去这样转换。 +先让你看看深渊长什么样,开开眼,然后你再决定是否深入: `mem::transmute` 将类型 `T` 直接转成类型 `U`,唯一的要求就是,这两个类型占用同样大小的字节数!我的天,这也算限制?这简直就是无底线的转换好吧?看看会导致什么问题: +1. 首先也是最重要的,转换后创建一个任意类型的实例会造成无法想象的混乱,而且根本无法预测。不要把 `3` 转换成 `bool` 类型,就算你根本不会去使用该 `bool` 类型,也不要去这样转换 2. 变形后会有一个重载的返回类型,即使你没有指定返回类型,为了满足类型推导的需求,依然会产生千奇百怪的类型 -3. 将`&`变形为`&mut`是未定义的行为 +3. 将 `&` 变形为 `&mut` 是未定义的行为 - 这种转换永远都是未定义的 - 不,你不能这么做 - 不要多想,你没有那种幸运 4. 变形为一个未指定生命周期的引用会导致[无界生命周期](../advance/lifetime/advance.md) -5. 在复合类型之间互相变换时,你需要保证它们的排列布局是一模一样的!一旦不一样,那么字段就会得到不可预期的值,这也是未定义的行为,至于你会不会因此愤怒,who cares,你都用了变形了,老兄! +5. 在复合类型之间互相变换时,你需要保证它们的排列布局是一模一样的!一旦不一样,那么字段就会得到不可预期的值,这也是未定义的行为,至于你会不会因此愤怒, **WHO CARES** ,你都用了变形了,老兄! -对于第5条,你该如何知道内存的排列布局是一样的呢?对于`repr(C)`类型和`repr(transparent)`类型来说,它们的布局是有着精确定义的。但是对于你自己的"普通却自信"的Rust类型`repr(Rust)`来说,它可不是有着精确定义的。甚至同一个泛型类型的不同实例都可以有不同的内存布局。`Vec`和`Vec`它们的字段可能有着相同的顺序,也可能没有。对于数据排列布局来说,什么能保证,什么不能保证目前还在Rust开发组的[工作任务](https://rust-lang.github.io/unsafe-code-guidelines/layout.html)中呢. +对于第5条,你该如何知道内存的排列布局是一样的呢?对于 `repr(C)` 类型和 `repr(transparent)` 类型来说,它们的布局是有着精确定义的。但是对于你自己的"普通却自信"的 Rust 类型 `repr(Rust)` 来说,它可不是有着精确定义的。甚至同一个泛型类型的不同实例都可以有不同的内存布局。 `Vec` 和 `Vec` 它们的字段可能有着相同的顺序,也可能没有。对于数据排列布局来说,**什么能保证,什么不能保证**目前还在 Rust 开发组的[工作任务](https://rust-lang.github.io/unsafe-code-guidelines/layout.html)中呢。 -你以为你之前凝视的是深渊吗?不,你凝视的只是深渊的大门。`mem::transmute_copy`才是真正的深渊,它比之前的还要更加危险和不安全。它从`T`类型中拷贝出`U`类型所需的字节数,然后转换成`U`。`mem::transmute`尚有大小检查,能保证两个数据的内存大小一致,现在这哥们干脆连这个也丢了,只不过`U`的尺寸若是比`T`大,会是一个未定义行为。 +你以为你之前凝视的是深渊吗?不,你凝视的只是深渊的大门。 `mem::transmute_copy` 才是真正的深渊,它比之前的还要更加危险和不安全。它从 `T` 类型中拷贝出 `U` 类型所需的字节数,然后转换成 `U`。 `mem::transmute` 尚有大小检查,能保证两个数据的内存大小一致,现在这哥们干脆连这个也丢了,只不过 `U` 的尺寸若是比 `T` 大,会是一个未定义行为。 -当然,你也可以通过原生指针转换和`unions`(todo!)获得所有的这些功能,但是你将无法获得任何编译提示或者检查。原生指针转换和`unions`也不是魔法,无法逃避上面说的规则。 +当然,你也可以通过原生指针转换和 `unions` (todo!)获得所有的这些功能,但是你将无法获得任何编译提示或者检查。原生指针转换和 `unions` 也不是魔法,无法逃避上面说的规则。 diff --git a/book/contents/basic/method.md b/book/contents/basic/method.md index 4989c56a..8f89d1fd 100644 --- a/book/contents/basic/method.md +++ b/book/contents/basic/method.md @@ -1,14 +1,14 @@ # 方法Method -从面向对象语言过来的同学对于方法肯定不陌生,`class` 里面就充斥着方法的概念。在Rust中,方法的概念也大差不差,往往和对象成对出现: +从面向对象语言过来的同学对于方法肯定不陌生,`class` 里面就充斥着方法的概念。在 Rust 中,方法的概念也大差不差,往往和对象成对出现: ```rust object.method() ``` -例如读取一个文件写入缓冲区,如果用函数的写法 `read(f,buffer)`,用方法的写法 `f.read(buffer)`。不过与其它语言 `class` 跟方法的联动使用不同(这里可能要修改下),Rust的方法往往跟结构体、枚举、特征一起使用,特征(Trait)将在后面几章进行介绍。 +例如读取一个文件写入缓冲区,如果用函数的写法 `read(f,buffer)`,用方法的写法 `f.read(buffer)`。不过与其它语言 `class` 跟方法的联动使用不同(这里可能要修改下),Rust 的方法往往跟结构体、枚举、特征一起使用,特征(Trait)将在后面几章进行介绍。 ## 定义方法 -Rust使用`impl`来定义方法,例如以下代码: +Rust 使用 `impl` 来定义方法,例如以下代码: ```rust struct Circle { x: f64, @@ -34,11 +34,11 @@ impl Circle { } ``` -我们这里先不详细展开讲解,只是先建立对方法定义的大致印象。下面的图片将Rust方法定义与其它语言的方法定义做了对比: +我们这里先不详细展开讲解,只是先建立对方法定义的大致印象。下面的图片将 Rust 方法定义与其它语言的方法定义做了对比: -可以看出,其它语言中所有定义都在 `class` 中,但是Rust的对象定义和方法定义是分离的,这种数据和使用分离的方式,会给予使用者极高的灵活度。 +可以看出,其它语言中所有定义都在 `class` 中,但是 Rust 的对象定义和方法定义是分离的,这种数据和使用分离的方式,会给予使用者极高的灵活度。 再来看一个例子: ```rust @@ -66,16 +66,16 @@ fn main() { 该例子定义了一个 `Rectangle` 结构体,并且在其上定义了一个 `area` 方法,用于计算该矩形的面积。 -`impl Rectangle {}` 表示为 `Rectangle` 实现方法(`impl` 是实现*implementation* 的缩写),这样的写法表明 `impl` 语句块中的一切都是跟 `Rectangle` 相关联的。 +`impl Rectangle {}` 表示为 `Rectangle` 实现方法(`impl` 是实现 *implementation* 的缩写),这样的写法表明 `impl` 语句块中的一切都是跟 `Rectangle` 相关联的。 -接下里的内容非常重要,请大家仔细看。在 `area` 的签名中,我们使用 `&self` 替代 `rectangle: &Rectangle`,`&self` 其实是 `self: &Self` 的简写(注意大小写)。在一个 `impl` 块内,`Self` 指代被实现方法的结构体类型,`self` 指代此类型的实例,换句话说,`self` 指代的是 `Rectangle` 结构体实例,这样的写法会让我们的代码简洁很多,而且非常便于理解: 我们为哪个结构体实现方法,那么`self`就是指代哪个结构体的实例。 +接下里的内容非常重要,请大家仔细看。在 `area` 的签名中,我们使用 `&self` 替代 `rectangle: &Rectangle`,`&self` 其实是 `self: &Self` 的简写(注意大小写)。在一个 `impl` 块内,`Self` 指代被实现方法的结构体类型,`self` 指代此类型的实例,换句话说,`self` 指代的是 `Rectangle` 结构体实例,这样的写法会让我们的代码简洁很多,而且非常便于理解:我们为哪个结构体实现方法,那么 `self` 就是指代哪个结构体的实例。 需要注意的是,`self` 依然有所有权的概念: - `self` 表示 `Rectangle` 的所有权转移到该方法中,这种形式用的较少 - `&self` 表示该方法对 `Rectangle` 的不可变借用 - `&mut self` 表示可变借用 -总之,`self` 的使用就跟函数参数一样,要严格遵守Rust的所有权规则。 +总之,`self` 的使用就跟函数参数一样,要严格遵守 Rust 的所有权规则。 回到上面的例子中,选择 `&self` 的理由跟在函数中使用 `&Rectangle` 是相同的:我们并不想获取所有权,也无需去改变它,只是希望能够读取结构体中的数据。如果想要在方法中去改变当前的结构体,需要将第一个参数改为 `&mut self`。仅仅通过使用 `self` 作为第一个参数来使方法获取实例的所有权是很少见的,这种使用方式往往用于把当前的对象转成另外一个对象时使用,转换完后,就不再关注之前的对象,且可以防止对之前对象的误调用。 @@ -84,7 +84,7 @@ fn main() { - 代码的组织性和内聚性更强,对于代码维护和阅读来说,好处巨大 #### 方法名跟结构体字段名相同 -在Rust中,允许方法名跟结构体的字段名相同: +在 Rust 中,允许方法名跟结构体的字段名相同: ```rust impl Rectangle { fn width(&self) -> bool { @@ -104,7 +104,7 @@ fn main() { } ``` -当我们使用 `rect1.width()` 时,Rust知道我们调用的是它的方法,如果使用 `rect1.witdh`,则是访问它的字段。 +当我们使用 `rect1.width()` 时, Rust 知道我们调用的是它的方法,如果使用 `rect1.witdh`,则是访问它的字段。 一般来说,方法跟字段同名,往往适用于实现 `getter` 访问器,例如: ```rust @@ -129,7 +129,7 @@ fn main() { } ``` -用这种方式,我们可以把 `Rectangle` 的字段设置为私有属性,只需把它的`new`和`width`方法设置为公开可见,那么用户就可以创建一个矩形,同时通过访问器`rect1.width()` 方法来获取矩形的宽度,因为 `width` 字段是私有的,当用户访问 `rect1.width` 字段时,就会报错。注意在此例中,`Self` 指代的就是被实现方法的结构体 `Rectangle`。 +用这种方式,我们可以把 `Rectangle` 的字段设置为私有属性,只需把它的 `new` 和 `width` 方法设置为公开可见,那么用户就可以创建一个矩形,同时通过访问器 `rect1.width()` 方法来获取矩形的宽度,因为 `width` 字段是私有的,当用户访问 `rect1.width` 字段时,就会报错。注意在此例中,`Self` 指代的就是被实现方法的结构体 `Rectangle`。 > ### `->` 运算符到哪去了? > @@ -188,11 +188,11 @@ fn main() { ## 关联函数 -现在大家可以思考一个问题,如何为一个结构体定义一个构造器方法?也就是接受几个参数,然后构造并返回该结构体的实例。其实答案在开头的代码片段中就给出了,很简单,不使用`self`中即可。 +现在大家可以思考一个问题,如何为一个结构体定义一个构造器方法?也就是接受几个参数,然后构造并返回该结构体的实例。其实答案在开头的代码片段中就给出了,很简单,不使用 `self` 中即可。 这种定义在 `impl` 中且没有 `self` 的函数被称之为**关联函数**: 因为它没有 `self`,不能用 `f.read()` 的形式调用,因此它是一个函数而不是方法,它又在`impl` 中,与结构体紧密关联,因此称为关联函数。 -在之前的代码中,我们已经多次使用过关联函数,例如`String::from`,用于创建一个动态字符串。 +在之前的代码中,我们已经多次使用过关联函数,例如 `String::from`,用于创建一个动态字符串。 ```rust # #[derive(Debug)] @@ -208,12 +208,12 @@ impl Rectangle { } ``` -> Rust中有一个约定俗称的规则,使用`new`来作为构造器的名称,出于设计上的考虑,Rust特地没有用`new`作为关键字 +> Rust 中有一个约定俗称的规则,使用 `new` 来作为构造器的名称,出于设计上的考虑,Rust 特地没有用 `new` 作为关键字 -因为是函数,所以不能用`.`的方式来调用,我们需要用`::`来调用,例如 `let sq = Rectangle::new(3,3);`。这个方法位于结构体的命名空间中:`::` 语法用于关联函数和模块创建的命名空间。 +因为是函数,所以不能用 `.` 的方式来调用,我们需要用`::`来调用,例如 `let sq = Rectangle::new(3,3);`。这个方法位于结构体的命名空间中:`::` 语法用于关联函数和模块创建的命名空间。 ## 多个impl定义 -Rust允许我们为一个结构体定义多个`impl`块,目的是提供更多的灵活性和代码组织性,例如当方法多了后,可以把相关的方法组织在同一个`impl`块中,那么就可以形成多个`impl`块,各自完成一块儿目标: +Rust 允许我们为一个结构体定义多个 `impl` 块,目的是提供更多的灵活性和代码组织性,例如当方法多了后,可以把相关的方法组织在同一个 `impl` 块中,那么就可以形成多个 `impl` 块,各自完成一块儿目标: ```rust # #[derive(Debug)] # struct Rectangle { @@ -234,7 +234,7 @@ impl Rectangle { } ``` -当然,就这个例子而言,我们没必要使用两个`impl`块,这里只是为了演示方便。 +当然,就这个例子而言,我们没必要使用两个 `impl` 块,这里只是为了演示方便。 ## 为枚举实现方法 diff --git a/book/contents/first-try/editor.md b/book/contents/first-try/editor.md index a6b6dc77..c01e8d1f 100644 --- a/book/contents/first-try/editor.md +++ b/book/contents/first-try/editor.md @@ -8,7 +8,7 @@ 2. 在 15 年预言 `VSCode` 会成为世界上最好的 IDE;同时我还是 `jaeger tracing` 项目的第一个 star 用户(是的,比作者还早),当时就很看好这个项目的后续发展。 3. 现在呢,我在这里正式预言: **未来 `Rust` 会成为主流编程语言之一,在几乎所有开发领域都将大放光彩**。总之牛逼已吹下,希望不要被打脸。:( -下面继续简单介绍下 VScode,以下内容引用于官网: +下面继续简单介绍下 VSCode,以下内容引用于官网: > Visual Studio Code(VSCode) 是微软 2015 年推出的一个轻量但功能强大的源代码编辑器,基于 Electron 开发,支持 Windows、Linux 和 MacOS 操作系统。它内置了对 JavaScript,TypeScript 和 Node.js 的支持并且具有丰富的其它语言和扩展的支持,功能超级强大。Visual Studio Code 是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流的开发语言的语法高亮、智能代码补全、自定义快捷键、括号匹配和颜色区分、代码片段、代码对比 Diff、GIT 命令等特性,支持插件扩展,并针对网页开发和云端应用开发做了优化。 diff --git a/book/contents/first-try/intro.md b/book/contents/first-try/intro.md index d616d7fb..bac43f4b 100644 --- a/book/contents/first-try/intro.md +++ b/book/contents/first-try/intro.md @@ -5,6 +5,6 @@ 在本章中,你将学习以下内容: 1. 在 MacOS、Linux、Windows 上安装 Rust 以及相关工具链 -2. 搭建 Vscode 所需的环境 +2. 搭建 VSCode 所需的环境 3. 简单介绍 Cargo 4. 实现一个酷炫多国语言版本的“世界,你好”的程序,并且谈谈对 Rust 语言的初印象