diff --git a/SUMMARY.md b/SUMMARY.md index c78ed68..051846b 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -100,7 +100,7 @@ * [评测 (benchmark)](testing/bench.md) * [代码风格](coding-style/style.md)「tiansiyuan」 * [Any与反射](any/any.md)「wayslog」 -* [安全(safe)](safe/safety.md)「daogangtang」 +* [安全](safe/safety.md)「daogangtang」 * [常用数据结构实现](data-structure/preface.md)「Naupio」 * [栈结构](data-structure/stack.md) * [队列](data-structure/queue.md) diff --git a/action/db/readme.md b/action/db/readme.md index 63fca1e..97f4ca7 100644 --- a/action/db/readme.md +++ b/action/db/readme.md @@ -1,16 +1,16 @@ # rust数据库操作 -编程时,我们依赖数据库来存储相应的数据,很多编程语言都支持对数据库的操作(为什么不是全部,因为还有html这样的东西),所以当然 -可以使用rust操作数据库。 +编程时,我们依赖数据库来存储相应的数据,很多编程语言都支持对数据库的操作,所以当然可以使用Rust操作数据库。 -不过在我自己操作时,发现很多问题,主要因为我不了解rust在操作数据库时,应该注意的事情,从而浪费了很多的时间,在进行数据查询时。 +不过在我自己操作时,发现很多问题,主要因为我不了解Rust在操作数据库时,应该注意的事情,从而浪费了很多的时间,在进行数据查询时。 具体遇到的坑,我会做一些演示,从而让大家避免这些情况。 -首先使用rust操作postgresql,因为postgresql是我最喜欢的数据库。 +首先使用Rust操作PostgreSQL,因为PostgreSQL是我最喜欢的数据库。 首先创建新项目 `cargo new db --bin` -在cargo.toml中添加,postgres: +在cargo.toml中添加 `postgres` 如下: + ``` rust [package] @@ -185,7 +185,7 @@ fn main() { 自己遇到的坑 - 创建连接函数时,连接必须有一个返回值,所以必须指定返回值的类型, -对于一个写python的人而言,我觉得是痛苦的,我想按照官方的写法match +对于一个写Python的人而言,我觉得是痛苦的,我想按照官方的写法match 一下,发现可能产生多个返回值。在编译时直接无法通过编译,所以最终 使用了unwrap,解决问题,不过我还是没有学会,函数多值返回时我如何 定义返回值 @@ -224,8 +224,8 @@ Could not compile `db`. 然后去查看了关于postgres模块的所有函数,尝试了无数种办法,依旧没有解决。 -可能自己眼高手低,如果从头再把rust的相关教程看一下,可能很早就发现这个问题, -也有可能是因为习惯了写python,导致自己使用固有的思维来看待问题和钻牛角尖,才 +可能自己眼高手低,如果从头再把Rust的相关教程看一下,可能很早就发现这个问题, +也有可能是因为习惯了写Python,导致自己使用固有的思维来看待问题和钻牛角尖,才 导致出现这样的问题,浪费很多的时间。 - 改变思维,把自己当作一个全新的新手,既要利用已有的思想来学习新的语言,同样不要 diff --git a/action/json_data/readme.md b/action/json_data/readme.md index 4007a62..1ab6a32 100644 --- a/action/json_data/readme.md +++ b/action/json_data/readme.md @@ -1,10 +1,10 @@ -# rust json处理 +# Rust json处理 -json是一种比较重要的格式,尤其是现在的web开发领域,json相比于传统的xml更加容易操作和减小传输。 +JSON是一种比较重要的格式,尤其是现在的web开发领域,JSON相比于传统的XML更加容易操作和减小传输。 -rust中的json处理依赖 cargo 中的rustc-serialize模块 +Rust中的JSON处理依赖 cargo 中的rustc-serialize模块 -###先简单的创建一个rust项目工程 +###先简单的创建一个Rust项目工程 ``` rust $ cargo new json_data --bin @@ -64,7 +64,7 @@ $ cargo build *注意一个问题由于国内网络访问github不稳定,这些第三方库很多托管在github上,所以可能需要修改你的 网络访问* -1. 在安装rust之后,会在你的用户目录之下生成一个`.cargo`文件夹,进入这个文件夹 +1. 在安装Rust之后,会在你的用户目录之下生成一个`.cargo`文件夹,进入这个文件夹 2. 在`.cargo`文件夹下,创建一个`config`文件,在文件中填写中科大软件源,可能以后会出现其他的源,先用这个 3. `config`文件内容如下 diff --git a/ffi/compiling-rust-to-lib.md b/ffi/compiling-rust-to-lib.md index 69389c1..551d9e5 100644 --- a/ffi/compiling-rust-to-lib.md +++ b/ffi/compiling-rust-to-lib.md @@ -1,4 +1,4 @@ -# 将rust编译成库 +# 将Rust编译成库 上一章讲述了如何从rust中调用c库,这一章我们讲如何把rust编译成库让别的语言通过cffi调用。 ## 调用约定和mangle diff --git a/iterator/iterator.md b/iterator/iterator.md index 8c71730..aa0a85f 100644 --- a/iterator/iterator.md +++ b/iterator/iterator.md @@ -128,7 +128,7 @@ let m = (1..20).fold(1u64, |mul, x| mul*x); ### 适配器 -我们所熟知的生产消费的模型里,生产者所生产的东西不一定都会被消费者买账,因此,需要对原有的产品进行再组装。这个再组装的过程,就是适配器。因为适配器返回的是一个新的迭代器,可以直接用链式请求一直写下去,而不至于陷入到某前端语言的回调地狱之中。 +我们所熟知的生产消费的模型里,生产者所生产的东西不一定都会被消费者买账,因此,需要对原有的产品进行再组装。这个再组装的过程,就是适配器。因为适配器返回的是一个新的迭代器,所以可以直接用链式请求一直写下去。 前面提到了 Reduce 函数,那么自然不得不提一下另一个配套函数 —— `map` : @@ -149,7 +149,7 @@ warning: unused result which must be used: iterator adaptors are lazy and 呀,这是啥? -因为,所有的适配器,都是惰性求值的!都是惰性求值的!都是惰性求值的! +因为,所有的适配器,都是惰性求值的! **也就是说,除非你调用一个消费者,不然,你的操作,永远也不会被调用到!** diff --git a/module/module.md b/module/module.md index 5ba6c96..c80c743 100644 --- a/module/module.md +++ b/module/module.md @@ -263,7 +263,7 @@ use super::xxx; 路径中的 `*` 符号: ```rust -use xxx:* +use xxx::*; ``` 表示导入 `xxx` 模块下的所有可见 item(加了 pub 标识的 item)。 diff --git a/ownership-system/lifetime.md b/ownership-system/lifetime.md index 2bb631b..f5fc7ab 100644 --- a/ownership-system/lifetime.md +++ b/ownership-system/lifetime.md @@ -18,7 +18,7 @@ fn main() { > error: unresolved name `x`. -错误的意思是“无法解析 `a` 标识符”,也就是找不到 `x` , 这是因为像很多编程语言一样,Rust中也存在作用域概念,当资源离开离开作用域后,资源的内存就会被释放回收,当借用/引用离开作用域后也会被销毁,所以 `x` 在离开自己的作用域后,无法在作用域之外访问。 +错误的意思是“无法解析 `x` 标识符”,也就是找不到 `x` , 这是因为像很多编程语言一样,Rust中也存在作用域概念,当资源离开离开作用域后,资源的内存就会被释放回收,当借用/引用离开作用域后也会被销毁,所以 `x` 在离开自己的作用域后,无法在作用域之外访问。 上面的涉及到几个概念: diff --git a/ownership-system/ownership.md b/ownership-system/ownership.md index b017b1b..4c92a96 100644 --- a/ownership-system/ownership.md +++ b/ownership-system/ownership.md @@ -82,9 +82,9 @@ c.rs:4 println!("{}", a); 在Rust中,和“绑定”概念相辅相成的另一个机制就是“转移move所有权”,意思是,**可以把资源的所有权(ownership)从一个绑定转移(move)成另一个绑定**,这个操作同样通过`let`关键字完成,和绑定不同的是,`=`两边的左值和右值均为两个标识符: ```rust 语法: - let 标识符A = 标识符B; // 把“A”绑定资源的所有权转移给“B” + let 标识符A = 标识符B; // 把“B”绑定资源的所有权转移给“A” ``` -Move前后的内存示意如下: +move前后的内存示意如下: > **Before move:** a <=> 内存(地址:**A**,内容:"xyz") **After move:** diff --git a/quickstart/control-flow.md b/quickstart/control-flow.md index ff8eded..f5a4b12 100644 --- a/quickstart/control-flow.md +++ b/quickstart/control-flow.md @@ -118,8 +118,7 @@ match pair { } ``` -`match`的这种解构同样适用于结构体或者枚举。如果有必要, -还可以使用`..`来忽略域或者数据: +`match`的这种解构同样适用于结构体或者枚举。如果有必要,还可以使用`..`来忽略域或者数据: ```rust struct Point { @@ -141,6 +140,7 @@ enum OptionalInt { let x = OptionalInt::Value(5); match x { + // 这里是 match 的 if guard 表达式,我们将在以后的章节进行详细介绍 OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"), OptionalInt::Value(..) => println!("Got an int!"), OptionalInt::Missing => println!("No such luck."), diff --git a/quickstart/rust-travel.md b/quickstart/rust-travel.md index 0c75b10..38b3ada 100644 --- a/quickstart/rust-travel.md +++ b/quickstart/rust-travel.md @@ -65,8 +65,8 @@ fn main() { > [package] name = "hellorust" version = "0.1." -authors = ["YourName "] -> [dependencies] +authors = ["YourName "] +[dependencies] - 编辑src目录下的main.rs文件 diff --git a/quickstart/struct-enum.md b/quickstart/struct-enum.md index a4f043a..adca942 100644 --- a/quickstart/struct-enum.md +++ b/quickstart/struct-enum.md @@ -15,7 +15,7 @@ struct Point { x: i32, y: i32, } -let mut point = Point { x: 0, y: 0 }; +let point = Point { x: 0, y: 0 }; // tuple structs struct Color(u8, u8, u8); diff --git a/testing/bench.md b/testing/bench.md index f329419..8dc000a 100644 --- a/testing/bench.md +++ b/testing/bench.md @@ -1,7 +1,7 @@ -# 性能评测 +# 性能测试 -测试,是验证程序的正确性,这是第一步。程序能正常运行后,往往需要测试程序(一部分)的执行速度,这时,就需要用到性能评测。 -通常来讲,所谓性能评测,指的是测量程序运行的速度,即运行一次要多少时间(通常是执行多次求平均值)。Rust 竟然连这个特性都集成在语言基础特性中,真的是一门很重视工程性的语言。 +测试,是验证程序的正确性,这是第一步。程序能正常运行后,往往需要测试程序(一部分)的执行速度,这时,就需要用到性能测试。 +通常来讲,所谓性能测试,指的是测量程序运行的速度,即运行一次要多少时间(通常是执行多次求平均值)。Rust 竟然连这个特性都集成在语言基础特性中,真的是一门很重视工程性的语言。 下面直接说明如何使用。 @@ -63,11 +63,11 @@ test tests::bench_add_two ... bench: 1 ns/iter (+/- 0) test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured ``` -可以看到,Rust 的性能评测是以纳秒 ns 为单位。 +可以看到,Rust 的性能测试是以纳秒 ns 为单位。 写测评代码的时候,需要注意以下一些点: -1. 只把你需要做性能评测的代码(函数)放在评测函数中; +1. 只把你需要做性能测试的代码(函数)放在评测函数中; 2. 对于参与做性能测试的代码(函数),要求每次测试做同样的事情,不要做累积和改变外部状态的操作; 3. 参数性能测试的代码(函数),执行时间不要太长。太长的话,最好分成几个部分测试。这也方便找出性能瓶颈所在地方。 diff --git a/type/compound-types.md b/type/compound-types.md index 18057c0..cbdfd91 100644 --- a/type/compound-types.md +++ b/type/compound-types.md @@ -2,7 +2,7 @@ ## 元组(Tuple) -在别的语言里,你可能听过元组这个词,它表示一个大小、类型固定的有序数据组。在Rust中,情况并没有什么本质上的不同。不过Rust为我们提供了一系列简单便利的语法来让我们能更好的使用他。 +在别的语言里,你可能听过元组这个词,它表示一个大小、类型固定的有序数据组。在 Rust 中,情况并没有什么本质上的不同。不过 Rust 为我们提供了一系列简单便利的语法来让我们能更好的使用他。 ```rust let y = (2, "hello world"); @@ -10,7 +10,7 @@ let x: (i32, &str) = (3, "world hello"); // 然后呢,你能用很简单的方式去访问他们: -// 用let表达式 +// 用 let 表达式 let (w, z) = y; // w=2, z="hello world" // 用下标 @@ -40,7 +40,7 @@ struct A { ### 元组类型结构体 -元组类型结构体使用小括号,类似 `tuple`。 +元组类型结构体使用小括号,类似 `tuple` 。 ```rust struct B(i32, u16, bool); @@ -58,8 +58,8 @@ struct D; 空结构体的内存占用为0。但是我们依然可以针对这样的类型实现它的“成员函数”。 -不过到目前为止,除了1.9 nightly版本外,空结构体后面不能加大括号。 -如果这么写,则会导致编译错误: +不过到目前为止,在 1.9 版本之前的版本,空结构体后面不能加大括号。 +如果这么写,则会导致这部分的老编译器编译错误: ```rust struct C { @@ -98,21 +98,21 @@ fn main() { } ``` -看见了self,Python程序员不厚道的笑了。 +看见了 `self`,Python程序员不厚道的笑了。 -我们来分析一下,上面的`impl`中,new被Person这个结构体自身所调用,其特征是`::`的调用,Java程序员站出来了:类函数! 而带有`self`的`greeting`,更像是一个成员函数。 +我们来分析一下,上面的`impl`中,new 被 Person 这个结构体自身所调用,其特征是 `::` 的调用,Java程序员站出来了:类函数! 而带有 `self` 的 `greeting` ,更像是一个成员函数。 恩,回答正确,然而不加分。 ### 关于各种ref的讨论 -Rust对代码有着严格的安全控制,因此对一个变量也就有了所有权和借用的概念。所有权同一时间只能一人持有,可变引用也只能同时被一个实例持有,不可变引用则可以被多个实例持有。同时所有权能被转移,在Rust中被称为`move`。 +Rust 对代码有着严格的安全控制,因此对一个变量也就有了所有权和借用的概念。所有权同一时间只能一人持有,可变引用也只能同时被一个实例持有,不可变引用则可以被多个实例持有。同时所有权能被转移,在Rust中被称为 `move` 。 -以上是所有权的基本概念,事实上,在整个软件的运行周期内,所有权的转换是一件极其恼人和烦琐的事情,尤其对那些初学Rust的同学来说。同样的,Rust的结构体作为其类型系统的基石,也有着比较严格的所有权控制限制。具体来说,关于结构体的所有权,有两种你需要考虑的情况。 +以上是所有权的基本概念,事实上,在整个软件的运行周期内,所有权的转换是一件极其恼人和烦琐的事情,尤其对那些初学 Rust 的同学来说。同样的,Rust 的结构体作为其类型系统的基石,也有着比较严格的所有权控制限制。具体来说,关于结构体的所有权,有两种你需要考虑的情况。 -#### 字段的ref和owner +#### 字段的 ref 和 owner -在以上的结构体中,我们定义了不少结构体,但是如你所见,结构体的每个字段都是完整的属于自己的。也就是说,每个字段的owner都是这个结构体。每个字段的生命周期最终都不会超过这个结构体。 +在以上的结构体中,我们定义了不少结构体,但是如你所见,结构体的每个字段都是完整的属于自己的。也就是说,每个字段的 owner 都是这个结构体。每个字段的生命周期最终都不会超过这个结构体。 但是有些时候,我只是想要持有一个(可变)引用的值怎么办? 如下代码: @@ -130,7 +130,7 @@ struct RefBoy { :6 loc: & i32, ``` -这种时候,你将持有一个值的引用,因为它本身的生命周期在这个struct之外,所以对这个结构体而言,它无法准确的判断获知这个引用的生命周期,这在Rust编译器而言是不被接受的。 +这种时候,你将持有一个值的引用,因为它本身的生命周期在这个结构体之外,所以对这个结构体而言,它无法准确的判断获知这个引用的生命周期,这在 Rust 编译器而言是不被接受的。 因此,这个时候就需要我们给这个结构体人为的写上一个生命周期,并显式地表明这个引用的生命周期。写法如下: ```rust @@ -139,7 +139,7 @@ struct RefBoy<'a> { } ``` -这里解释一下这个符号`<>`,它表示的是一个`属于`的关系,无论其中描述的是*生命周期*还是*泛型*。即: `RefBoy in 'a `。最终我们可以得出个结论,`RefBoy`这个结构体,其生命周期一定不能比`'a`更长才行。 +这里解释一下这个符号 `<>`,它表示的是一个 `属于` 的关系,无论其中描述的是 *生命周期* 还是 *泛型* 。即: `RefBoy in 'a `。最终我们可以得出个结论,`RefBoy` 这个结构体,其生命周期一定不能比 `'a` 更长才行。 写到这里,可能有的人还是对生命周期比较迷糊,不明白其中缘由,其实你只需要知道两点即可: @@ -148,17 +148,17 @@ struct RefBoy<'a> { 关于第二点,其实生命周期是可以写多个的,用 `,` 分隔。 -注:生命周期和泛型都写在`<>`里,先生命周期后泛型,用`;`分隔。 +注:生命周期和泛型都写在 `<>` 里,先生命周期后泛型,用`,`分隔。 #### impl中的三种self 前面我们知道,Rust中,通过impl可以对一个结构体添加成员方法。同时我们也看到了`self`这样的关键字,同时,这个self也有好几种需要你仔细记忆的情况。 -impl中的self,常见的有三种形式:`self`、`&self`、`&mut self` ,我们分别来说。 +impl中的self,常见的有三种形式:`self`、 `&self`、`&mut self` ,我们分别来说。 ##### 被move的self -正如上面例子中的impl,我们实现了一个以`self`为第一个参数的函数,但是这样的函数实际上是有问题的。 +正如上面例子中的impl,我们实现了一个以 `self` 为第一个参数的函数,但是这样的函数实际上是有问题的。 问题在于Rust的所有权转移机制。 我曾经见过一个关于Rust的笑话:"你调用了一下别人,然后你就不属于你了"。 @@ -189,9 +189,9 @@ fn main() { :13 println!("{}", ast.a); ``` -为什么呢?因为Rust本身,在你调用一个函数的时候,如果传入的不是一个引用,那么无疑,这个参数的owner将被move掉。同理,`impl`中的`self`,如果你写的不是一个引用的话,也是会被默认的move掉哟! +为什么呢?因为 Rust 本身,在你调用一个函数的时候,如果传入的不是一个引用,那么无疑,这个参数将被这个函数吃掉,即其 owner 将被 move 到这个函数的参数上。同理,`impl` 中的 `self` ,如果你写的不是一个引用的话,也是会被默认的 move 掉哟! -那么如何避免这种情况呢?答案是`Copy`和`Clone`: +那么如何避免这种情况呢?答案是 `Copy` 和 `Clone` : ```rust #[derive(Copy, Clone)] @@ -200,13 +200,13 @@ struct A { } ``` -这么写的话,会使编译通过。但是这么写实际上也是有其缺陷的。其缺陷就是:你不能在一个被copy的`impl`函数里改变它!事实上,被move的`self`其实是相对少用的一种情况,更多的时候,我们需要的是`ref`和`ref mut`。 +这么写的话,会使编译通过。但是这么写实际上也是有其缺陷的。其缺陷就是: `Copy` 或者 `Clone` ,都会带来一定的运行时开销!事实上,被move的 `self` 其实是相对少用的一种情况,更多的时候,我们需要的是 `ref` 和 `ref mut` 。 -###### ref和ref mut +###### ref 和 ref mut -关于`ref`和`mut ref`的写法和被move的`self`写法类似,只不过多了一个引用修饰符号,上面有例子,不多说。 +关于 `ref` 和 `mut ref` 的写法和被 move 的 `self` 写法类似,只不过多了一个引用修饰符号,上面有例子,不多说。 -需要注意的一点是,你不能在一个`ref`的方法里调用一个`mut ref`,任何情况下都不行! +需要注意的一点是,你不能在一个 `&self` 的方法里调用一个 `&mut ref` ,任何情况下都不行! 但是,反过来是可以的。代码如下: @@ -238,15 +238,15 @@ fn main() { } ``` -需要注意的是,一旦你的结构体持有一个可变引用,你,只能在`&mut self`的实现里去改变他! +需要注意的是,一旦你的结构体持有一个可变引用,你,只能在 `&mut self` 的实现里去改变他! -Rust允许我们灵活的对一个struct进行你想要的实现,在编程的自由度上无疑有了巨大的提高。 +Rust允许我们灵活的对一个 struct 进行你想要的实现,在编程的自由度上无疑有了巨大的提高。 -至于更高级的关于trait和泛型的用法,我们将在以后的章节进行详细介绍。 +至于更高级的关于 trait 和泛型的用法,我们将在以后的章节进行详细介绍。 ## 枚举类型 enum -Rust的枚举(`enum`)类型,跟C语言的枚举有点接近,然而更强大,事实上是代数数据类型(Algebraic Data Type)。 +Rust的枚举(`enum`)类型,跟C语言的枚举有点接近,然而更强大,事实上它是一种代数数据类型(Algebraic Data Type)。 比如说,这是一个代表东南西北四个方向的枚举: @@ -259,7 +259,7 @@ enum Direction { } ``` -但是,rust的枚举能做到的,比C语言的多很多。 +但是,rust 的枚举能做到的,比 C 语言的更多。 比如,枚举里面居然能包含一些你需要的,特定的数据信息! 这是常规的枚举所无法做到的,更像枚举类,不是么? @@ -284,7 +284,8 @@ enum SpecialPoint { ### 使用枚举 -和struct的成员访问符号`.`不同的是,枚举类型要想访问其成员,几乎无一例外的要用到模式匹配。并且, 你可以写一个`Direction::West`,但是你绝对不能写成`Direction.West`。虽然编译器足够聪明能发现你这个粗心的毛病。 +和struct的成员访问符号 `.` 不同的是,枚举类型要想访问其成员,几乎无一例外的要用到模式匹配。并且, 你可以写一个 `Direction::West`,但是你现在还不能写成 `Direction.West`, 除非你显式的 `use` 它 。虽然编译器足够聪明能发现你这个粗心的毛病。 + 关于模式匹配,我不会说太多,还是举个栗子 @@ -308,7 +309,7 @@ fn main() { ``` 呐呐呐,这就是模式匹配取值啦。 -当然了,`enum`其实也是可以`impl`的,一般人我不告诉他! +当然了, `enum` 其实也是可以 `impl` 的,一般人我不告诉他! 对于带有命名字段的枚举,模式匹配时可指定字段名 @@ -321,7 +322,7 @@ match sp { } ``` -对于带有字段名的枚举类型,其模式匹配语法与匹配`struct`时一致。如 +对于带有字段名的枚举类型,其模式匹配语法与匹配 `struct` 时一致。如 ```rust struct Point { @@ -338,4 +339,4 @@ let Point { x, y } = point; let Point { x: x, .. } = point; ``` -模式匹配的语法与`if let`和`let`是一致的,所以在后面的内容中看到的也支持同样的语法。 +模式匹配的语法与 `if let` 和 `let` 是一致的,所以在后面的内容中看到的也支持同样的语法。 diff --git a/type/operator-and-formatting.md b/type/operator-and-formatting.md index c56c158..4400284 100644 --- a/type/operator-and-formatting.md +++ b/type/operator-and-formatting.md @@ -183,7 +183,7 @@ parameter := integer '$' 最后,留个作业吧。 给出参数列表如下: -`(500.0, 12, "ELTON", "QB", 8, CaiNiao="Mike")` +`(500.0, 12, "ELTON", "QB", 4, CaiNiao="Mike")` 请写出能最后输出一下句子并且将参数*都*被用过*至少一遍*的格式化字符串,并自己去play实验一下。 diff --git a/type/string.md b/type/string.md index 5b07c6c..ed685da 100644 --- a/type/string.md +++ b/type/string.md @@ -59,7 +59,7 @@ fn main() { 我们来分析一下,以下部分将涉及到部分`Deref`的知识,可能需要你预习一下,如果不能理解大可跳过下一段: -首先呢, `&*`是两个符号`&`和`*`的集合,按照Rust的运算顺序,先对`String`进行`Deref`,也就是`*`操作。 +首先呢, `&*`是两个符号`&`和`*`的组合,按照Rust的运算顺序,先对`String`进行`Deref`,也就是`*`操作。 由于`String`实现了 `impl Deref for String`,这相当于一个运算符重载,所以你就能通过`*`获得一个`str`类型。但是我们知道,单独的`str`是不能在Rust里直接存在的,因此,我们需要先给他进行`&`操作取得`&str`这个结果。