Merge pull request #292 from AllanDowney/patch-7

Update: unified format
pull/298/head
Sunface 3 years ago committed by GitHub
commit 31158d8a20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -35,7 +35,7 @@ $ cargo run
字符'中'占用了4字节的内存大小
```
> 注意我们还没开始讲字符串但是这里提前说一下和一些语言不同Rust的字符只能用`''`来表示,`""`是留给字符串的
> 注意我们还没开始讲字符串但是这里提前说一下和一些语言不同Rust 的字符只能用 `''` 来表示, `""` 是留给字符串的
## 布尔(bool)
@ -61,8 +61,8 @@ fn main() {
只能说,再不起眼的东西,都有其用途,在目前为止的学习过程中,大家已经看到过很多次 `fn main()` 函数的使用吧?那么这个函数返回什么呢?
没错,`main`函数就返回这个元类型`()`,你不能说`main`函数无返回值,因为没有返回值的函数在Rust中是有单独的定义的`发散函数`,顾名思义,无法收敛的函数.
没错, `main` 函数就返回这个元类型 `()`,你不能说 `main` 函数无返回值,因为没有返回值的函数在 Rust 中是有单独的定义的:`发散函数`,顾名思义,无法收敛的函数。
例如常见的 `println!()` 的返回值也是元类型 `()`
再比如,你可以用`()`作为`map`的值,表示我们不关注具体的值,只关注`key`。 这种用法和Go语言的`struct{}`类似,可以作为一个值用来占位,但是完全不占用任何内存。
再比如,你可以用 `()` 作为 `map` 的值,表示我们不关注具体的值,只关注 `key`。 这种用法和 Go 语言的 ***struct{}*** 类似,可以作为一个值用来占位,但是完全**不占用**任何内存。

@ -2,7 +2,7 @@
Rust 的函数我们在之前已经见过不少,跟其他语言几乎没有什么区别。因此本章的学习之路将轻松和愉快,骚年们,请珍惜这种愉快,下一章你将体验到不一样的 Rust。
在函数界,有一个函数只闻其名不闻其声,可以止小孩啼,在程序界只有`hello,world!`可以与之媲美,它就是`add`函数:
在函数界,有一个函数只闻其名不闻其声,可以止小孩啼!在程序界只有 `hello,world!` 可以与之媲美,它就是 `add` 函数:
```rust
fn add(i: i32, j: i32) -> i32 {
@ -10,7 +10,7 @@ fn add(i: i32, j: i32) -> i32 {
}
```
该函数如此简单,但是又是如此的五脏俱全,声明函数的关键字`fn`,函数名`add()`,参数`i`和`j`,参数类型和返回值类型都是`i32`,总之一切那么的普通,但是又那么的自信,直到你看到了下面这张图:
该函数如此简单,但是又是如此的五脏俱全,声明函数的关键字 `fn` ,函数名 `add()`,参数 `i``j`,参数类型和返回值类型都是 `i32`,总之一切那么的普通,但是又那么的自信,直到你看到了下面这张图:
<img alt="" src="/img/function-01.png" class="center" />
@ -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,7 +68,7 @@ help: if this is a type, explicitly ignore the parameter name // 如果y是一
```
## 函数返回
在上一章节语句和表达式中,我们有提到在在: Rust中函数就是表达式,因此我们可以把函数的返回值直接赋给调用者。
在上一章节语句和表达式中,我们有提到,在 Rust 中函数就是表达式,因此我们可以把函数的返回值直接赋给调用者。
函数的返回值就是函数体最后一条表达式的返回值,当然我们也可以使用 `return` 提前返回,下面的函数使用最后一条表达式来返回一个值:
```rust
@ -88,7 +89,7 @@ fn main() {
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 {
@ -119,7 +120,7 @@ fn main() {
- 通过 `;` 结尾的表达式返回一个 `()`
例如下面的`report`函数会隐式返回一个`()`:
例如下面的 `report` 函数会隐式返回一个 `()`
```rust
use std::fmt::Debug;
@ -157,7 +158,7 @@ error[E0308]: mismatched types // 类型不匹配
| - help: consider removing this semicolon
```
还记得我们在[语句与表达式](./statement-expression.md)中讲过的吗?只有表达式能返回值,而`;`结尾的是语句在Rust中一定要严格区分表达式和语句的区别这个在其它语言中往往是被忽视的点。
还记得我们在[语句与表达式](./statement-expression.md)中讲过的吗?只有表达式能返回值,而 `;` 结尾的是语句,在 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;
@ -80,5 +81,5 @@ fn main() {
}
```
该语句块是表达式的原因是:它的最后一行是表达式,返回了`x + 1`的值,注意`x + 1`不能以分号结尾,否则就会从表达式变成语句, **表达式不能包含分号**。这一点非常重要,一旦你在表达式后加上分号,它就会变成一条语句,再也不会返回一个值,请牢记!
该语句块是表达式的原因是:它的最后一行是表达式,返回了 `x + 1` 的值,注意 `x + 1` 不能以分号结尾,否则就会从表达式变成语句 **表达式不能包含分号**。这一点非常重要,一旦你在表达式后加上分号,它就会变成一条语句,再也**不会**返回一个值,请牢记!

@ -164,7 +164,7 @@ error[E0277]: the trait bound `&mut i32: Trait` is not satisfied
再进一步,我们使用[完全限定语法](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<Target = U>`(`T`可以被解引用为`U`),那么编译器会使用`U`类型进行尝试,称之为**解引用方法调用**
3. 若上面两个方法依然不工作,编译器会试着解引用 `T` ,然后再进行尝试。这里使用了 `Deref` 特征 —— 若 `T: Deref<Target = U>` (`T` 可以被解引用为 `U`),那么编译器会使用 `U` 类型进行尝试,称之为**解引用方法调用**
4. 若 `T` 不能被解引用,且 `T` 是一个定长类型(在编译器类型长度是已知的),那么编译器也会尝试将 `T` 从定长类型转为不定长类型,例如将 `[i32; 2]` 转为 `[i32]`
5. 若还是不行,那...没有那了,最后编译器大喊一声:汝欺我甚,不干了!
@ -174,14 +174,14 @@ let array: Rc<Box<[T; 3]>> = ...;
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<Box<[T; 3]>>`是否有否实现`Index`特征,结果是否,不仅如此,`&Rc<Box<[T; 3]>> `与`&mut Rc<Box<[T; 3]>>`也没有实现.
`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<Box<[T; 3]>>` 是否有否实现 `Index` 特征,结果是否,不仅如此,`&Rc<Box<[T; 3]>>` 与 `&mut Rc<Box<[T; 3]>>` 也没有实现。
3. 上面的都不能工作,编译器开始对 `Rc<Box<[T; 3]>>` 进行解引用,把它转变成 `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`方法访问到对应的元素.
5. `[T; 3]` 以及它的各种引用都没有实现 `Index` 索引(是不是很反直觉:D在直觉中数组都可以通过索引访问实际上只有数组切片才可以!),它也不能再进行解引用,因此编译器只能祭出最后的大杀器:将定长转为不定长,因此 `[T; 3]` 被转换成 `[T]`,也就是数组切片,它实现了 `Index` 特征,因此最终我们可以通过 `index` 方法访问到对应的元素。
过程看起来很复杂,但是也还好挺好理解如果你先不能彻底理解也不要紧等以后对Rust理解更深了,同时需要深入理解类型转换时,再来细细品读本章。
过程看起来很复杂,但是也还好,挺好理解,如果你现在不能彻底理解,也不要紧,等以后对 Rust 理解更深了,同时需要深入理解类型转换时,再来细细品读本章。
再来看看以下更复杂的例子:
```rust
@ -189,9 +189,9 @@ fn do_stuff<T: Clone>(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<T>(value: &T) {
let cloned = value.clone();
@ -200,7 +200,7 @@ fn do_stuff<T>(value: &T) {
首先,从直觉上来说,该方法会报错,因为 `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`,只能去复制一个指针了。
@ -215,13 +215,13 @@ fn clone_containers<T>(foo: &Container<i32>, bar: &Container<T>) {
}
```
推断下上面的`foo_cloned`和`bar_cloned`是什么类型?提示: 关键在`Container`的泛型参数,一个是`i32`的具体类型,一个是泛型类型,其中`i32`实现了`Clone`,但是`T`并没有.
推断下上面的 `foo_cloned``bar_cloned` 是什么类型?提示: 关键在 `Container` 的泛型参数,一个是 `i32` 的具体类型,一个是泛型类型,其中 `i32` 实现了 `Clone`,但是 `T` 并没有。
首先要复习一下复杂类型派生`Clone`的规则:一个复杂类型能否派生`Clone`,需要它内部的所有子类型都能进行`Clone`。因此`Container<T>(Arc<T>)`是否实现`Clone`的关键在于`T`类型是否实现了`Clone`.
首先要复习一下复杂类型派生 `Clone` 的规则:一个复杂类型能否派生 `Clone`,需要它内部的所有子类型都能进行 `Clone`。因此 `Container<T>(Arc<T>)` 是否实现 `Clone` 的关键在于 `T` 类型是否实现了 `Clone` 特征。
上面代码中,`Container<i32>`实现了`Clone`特征,因此编译器可以直接进行值方法调用,此时相当于直接调用`foo.clone`,其中`clone`的函数签名是`fn clone(&T) -> T`,由此可以看出`foo_cloned`的类型是`Container<i32>`.
上面代码中,`Container<i32>` 实现了 `Clone` 特征,因此编译器可以直接进行值方法调用,此时相当于直接调用 `foo.clone`,其中 `clone` 的函数签名是 `fn clone(&T) -> T`,由此可以看出 `foo_cloned` 的类型是 `Container<i32>`
然而,`bar_cloned`的类型却是`&Container<T>`.这个不合理啊,明明我们为`Container<T>`派生了`Clone`特征,因此它也应该是`Container<T>`类型才对。万事皆有因,我们先来看下`derive`宏最终生成的代码大概是啥样的:
然而,`bar_cloned` 的类型却是 `&Container<T>`,这个不合理啊,明明我们为 `Container<T>` 派生了 `Clone` 特征,因此它也应该是 `Container<T>` 类型才对。万事皆有因,我们先来看下 `derive` 宏最终生成的代码大概是啥样的:
```rust
impl<T> Clone for Container<T> where T: Clone {
fn clone(&self) -> Self {
@ -230,11 +230,11 @@ impl<T> Clone for Container<T> where T: Clone {
}
```
从上面代码可以看出,派生`Clone`能实现的[根本是`T`实现了`Clone`特征](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable):`where T: Clone` 因此`Container<T>`就没有实现`Clone`特征。
从上面代码可以看出,派生 `Clone` 能实现的根本是 `T` 实现了[`Clone`特征](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable)`where T: Clone` 因此 `Container<T>` 就没有实现 `Clone` 特征。
编译器接着会去尝试引用方法调用,此时 `&Container<T>` 引用实现了 `Clone`,最终可以得出 `bar_cloned` 的类型是 `&Container<T>`
当然,也可以为`Container<T>`手动实现`Clone`特征:
当然,也可以为 `Container<T>` 手动实现 `Clone` 特征:
```rust
impl<T> Clone for Container<T> {
fn clone(&self) -> Self {
@ -243,9 +243,9 @@ impl<T> Clone for Container<T> {
}
```
此时,编译器首次尝试值方法调用即可通过,因此`bar_cloned`的类型变成`Container<T>`.
此时,编译器首次尝试值方法调用即可通过,因此 `bar_cloned` 的类型变成 `Container<T>`
这一块儿内容真的挺复杂每一个坚持看完的读者都是真正的勇士我也是为了写好这块儿内容作者足足花了4个小时
这一块儿内容真的挺复杂,每一个坚持看完的读者都是真正的勇士,我也是:为了写好这块儿内容,作者足足花了 **4** 个小时!
#### 变形记(Transmutes)
@ -253,17 +253,17 @@ impl<T> Clone for Container<T> {
类型系统,你让开!我要自己转换这些类型,不成功便成仁!虽然本书大多是关于安全的内容,我还是希望你能仔细考虑避免使用本章讲到的内容。这是你在 Rust 中所能做到的真真正正、彻彻底底、最最可怕的非安全行为,在这里,所有的保护机制都形同虚设。
先让你看看深渊长什么样,开开眼,然后你再决定是否深入: `mem::transmute<T, U>`将类型`T`直接转成类型`U`,唯一的要求就是,这两个类型占用同样大小的字节数!我的天,这也算限制?这简直就是无底线的转换好吧?看看会导致什么问题:
1. 首先也是最重要的,转换后创建一个任意类型的实例会造成无法想象的混乱,而且根本无法预测。不要把`3`转换成`bool`类型,就算你根本不会去使用该`bool`类型,也不要去这样转换
先让你看看深渊长什么样,开开眼,然后你再决定是否深入 `mem::transmute<T, U>` 将类型 `T` 直接转成类型 `U`,唯一的要求就是,这两个类型占用同样大小的字节数!我的天,这也算限制?这简直就是无底线的转换好吧?看看会导致什么问题:
1. 首先也是最重要的,转换后创建一个任意类型的实例会造成无法想象的混乱,而且根本无法预测。不要把 `3` 转换成 `bool` 类型,就算你根本不会去使用该 `bool` 类型,也不要去这样转换
2. 变形后会有一个重载的返回类型,即使你没有指定返回类型,为了满足类型推导的需求,依然会产生千奇百怪的类型
3. 将 `&` 变形为 `&mut` 是未定义的行为
- 这种转换永远都是未定义的
- 不,你不能这么做
- 不要多想,你没有那种幸运
4. 变形为一个未指定生命周期的引用会导致[无界生命周期](../advance/lifetime/advance.md)
5. 在复合类型之间互相变换时,你需要保证它们的排列布局是一模一样的!一旦不一样,那么字段就会得到不可预期的值,这也是未定义的行为,至于你会不会因此愤怒,who cares,你都用了变形了,老兄!
5. 在复合类型之间互相变换时,你需要保证它们的排列布局是一模一样的!一旦不一样,那么字段就会得到不可预期的值,这也是未定义的行为,至于你会不会因此愤怒, **WHO CARES** ,你都用了变形了,老兄!
对于第5条你该如何知道内存的排列布局是一样的呢对于`repr(C)`类型和`repr(transparent)`类型来说,它们的布局是有着精确定义的。但是对于你自己的"普通却自信"的Rust类型`repr(Rust)`来说,它可不是有着精确定义的。甚至同一个泛型类型的不同实例都可以有不同的内存布局。`Vec<i32>`和`Vec<u32>`它们的字段可能有着相同的顺序也可能没有。对于数据排列布局来说什么能保证什么不能保证目前还在Rust开发组的[工作任务](https://rust-lang.github.io/unsafe-code-guidelines/layout.html)中呢.
对于第5条你该如何知道内存的排列布局是一样的呢对于 `repr(C)` 类型和 `repr(transparent)` 类型来说,它们的布局是有着精确定义的。但是对于你自己的"普通却自信"的 Rust 类型 `repr(Rust)` 来说,它可不是有着精确定义的。甚至同一个泛型类型的不同实例都可以有不同的内存布局。 `Vec<i32>``Vec<u32>` 它们的字段可能有着相同的顺序,也可能没有。对于数据排列布局来说,**什么能保证,什么不能保证**目前还在 Rust 开发组的[工作任务](https://rust-lang.github.io/unsafe-code-guidelines/layout.html)中呢
你以为你之前凝视的是深渊吗?不,你凝视的只是深渊的大门。 `mem::transmute_copy<T, U>` 才是真正的深渊,它比之前的还要更加危险和不安全。它从 `T` 类型中拷贝出 `U` 类型所需的字节数,然后转换成 `U``mem::transmute` 尚有大小检查,能保证两个数据的内存大小一致,现在这哥们干脆连这个也丢了,只不过 `U` 的尺寸若是比 `T` 大,会是一个未定义行为。

@ -4,7 +4,7 @@
```rust
object.method()
```
例如读取一个文件写入缓冲区,如果用函数的写法 `read(f,buffer)`,用方法的写法 `f.read(buffer)`。不过与其它语言 `class` 跟方法的联动使用不同这里可能要修改下Rust的方法往往跟结构体、枚举、特征一起使用特征(Trait)将在后面几章进行介绍。
例如读取一个文件写入缓冲区,如果用函数的写法 `read(f,buffer)`用方法的写法 `f.read(buffer)`。不过与其它语言 `class` 跟方法的联动使用不同这里可能要修改下Rust 的方法往往跟结构体、枚举、特征一起使用,特征(Trait)将在后面几章进行介绍。
## 定义方法
@ -68,7 +68,7 @@ fn main() {
`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` 的所有权转移到该方法中,这种形式用的较少

@ -8,7 +8,7 @@
2. 在 15 年预言 `VSCode` 会成为世界上最好的 IDE同时我还是 `jaeger tracing` 项目的第一个 star 用户(是的,比作者还早),当时就很看好这个项目的后续发展。
3. 现在呢,我在这里正式预言: **未来 `Rust` 会成为主流编程语言之一,在几乎所有开发领域都将大放光彩**。总之牛逼已吹下,希望不要被打脸。:(
下面继续简单介绍下 VScode以下内容引用于官网
下面继续简单介绍下 VSCode以下内容引用于官网
> Visual Studio Code(VSCode) 是微软 2015 年推出的一个轻量但功能强大的源代码编辑器,基于 Electron 开发,支持 Windows、Linux 和 MacOS 操作系统。它内置了对 JavaScriptTypeScript 和 Node.js 的支持并且具有丰富的其它语言和扩展的支持功能超级强大。Visual Studio Code 是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流的开发语言的语法高亮、智能代码补全、自定义快捷键、括号匹配和颜色区分、代码片段、代码对比 Diff、GIT 命令等特性,支持插件扩展,并针对网页开发和云端应用开发做了优化。

@ -5,6 +5,6 @@
在本章中,你将学习以下内容:
1. 在 MacOS、Linux、Windows 上安装 Rust 以及相关工具链
2. 搭建 Vscode 所需的环境
2. 搭建 VSCode 所需的环境
3. 简单介绍 Cargo
4. 实现一个酷炫多国语言版本的“世界,你好”的程序,并且谈谈对 Rust 语言的初印象

Loading…
Cancel
Save