diff --git a/README.md b/README.md
index 373cf483..16d1ea1e 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,6 @@ Rust语言圣经是完全开源的电子书籍,但是也应该受到版权的
3. 知识链:知识B的学习如果需要先学习知识A,则A一定在B之前出现
4. 章节命名:当用户突然想了解某个知识点时,可以很快的定位到它所在的章节,例如想了解Arc,就应该`多线程 -> Arc`这种章节目录形式
-
## Rust社区
与国外的Rust发展如火如荼相比,国内的近况不是特别理想。
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index ac92f5f1..7ec4e00a 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -21,12 +21,12 @@
- [所有权和借用](basic/ownership/index.md)
- [所有权](basic/ownership/ownership.md)
- [引用与借用](basic/ownership/borrowing.md)
- - [字符串](basic/string.md)
+ - [字符串与切片](basic/string-slice.md)
- [复合类型(todo)](basic/compound-type/intro.md)
- [结构体(todo)](basic/compound-type/struct.md)
- [枚举](basic/compound-type/enum.md)
- [元组](basic/compound-type/tuple.md)
- - [数组与切片](basic/compound-type/array.md)
+ - [数组](basic/compound-type/array.md)
- [类型转换](basic/type-converse.md)
- [函数与方法(todo)](basic/function-method.md)
- [格式化输出(todo)](basic/formatted-output.md)
diff --git a/src/basic/ownership/borrowing.md b/src/basic/ownership/borrowing.md
index 3e529ef1..47aad455 100644
--- a/src/basic/ownership/borrowing.md
+++ b/src/basic/ownership/borrowing.md
@@ -221,17 +221,6 @@ fn main() {
虽然这种借用错误有的时候会让我们很郁闷,但是你只要想想这是Rust提前帮你发现了潜在的bug,其实就开心了,虽然减慢了开发速度,但是从长期来看,大幅减少了后续开发和运维成本.
-总的来说,借用的规则可以总结如下:
-1. 同一个作用域,特定数据可以有任意多个不可变借用
-2. 同一个作用域,特定数据最多只有一个可变借用
-3. 同一个作用域,特定数据不能同时拥有可变和不可变引用
-4. 借用在最后一次使用的地方被释放
-
-其实也不用死记硬背,你只要从安全性的角度稍微思考下,就能明白了,例如:有几个人同时在阅读一份在线文档,那么只要有一个人修改了,其它人看到的都会发生改变,这会造成错误的行为,对应上述的借用规则也就是:
-1. 如果没人修改,那么再多人观看这份文档都没问题
-2. 最多只能有一个人同时修改
-3. 如果有一个人能修改,那么其它人不应该在同时看这份文档
-
### 悬垂引用(Dangling References)
所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。
@@ -301,4 +290,11 @@ fn no_dangle() -> String {
}
```
-这样就没有任何错误了。所有权被移动出去,所以没有值被释放。
\ No newline at end of file
+这样就没有任何错误了。所有权被移动出去,所以没有值被释放。
+
+
+## 借用规则总结
+
+总的来说,借用规则如下:
+- 同一时刻,你只能拥有要么一个可变引用, 要么任意多个不可变引用
+- 引用必须总是有效的
\ No newline at end of file
diff --git a/src/basic/string-array-slice.md b/src/basic/string-array-slice.md
deleted file mode 100644
index 01765843..00000000
--- a/src/basic/string-array-slice.md
+++ /dev/null
@@ -1 +0,0 @@
-# string-array-slice.md
\ No newline at end of file
diff --git a/src/basic/string-slice.md b/src/basic/string-slice.md
new file mode 100644
index 00000000..2df390eb
--- /dev/null
+++ b/src/basic/string-slice.md
@@ -0,0 +1,352 @@
+# 字符串
+
+在其他语言,字符串往往是送分题,因为实在是太简单了,例如`"hello, world"`就是字符串章节的几乎全部内容了,对吧?如果你带着这样的想法来学Rust,
+我保证,绝对会栽跟头,**因此这一章大家一定要重视,仔细阅读,这里有很多其它Rust书籍中没有的内容**。
+
+首先来看段很简单的代码:
+```rust
+fn main() {
+ let my_name = "Pascal";
+ greet(my_name);
+}
+
+fn greet(name: String) {
+ println!("Hello, {}!", name);
+}
+```
+
+`greet`函数接受一个字符串类型的`name`参数,然后打印到终端控制台中,非常好理解,你们猜猜,这段代码能否通过编译?
+
+```conole
+error[E0308]: mismatched types
+ --> src/main.rs:3:11
+ |
+3 | greet(my_name);
+ | ^^^^^^^
+ | |
+ | expected struct `std::string::String`, found `&str`
+ | help: try using a conversion method: `my_name.to_string()`
+
+error: aborting due to previous error
+```
+
+Bingo,果然报错了,编译器提示`greet`函数需要一个`String`类型的字符串,却传入了一个`&str`类型的字符串,相信读者心中现在一定有几头草泥马呼啸而过,怎么字符串也能整出这么多花活?
+
+在讲解字符串之前,先来看看什么是切片?
+
+## 切片(slice)
+
+切片并不是Rust独有的概念,在Go语言中就非常流行,它允许你引用集合中一段连续的元素序列,而不是引用整个集合。
+
+对于字符串而言,切片就是对`String`类型中某一部分的引用,它看起来像这样:
+```rust
+let s = String::from("hello world");
+
+ let hello = &s[0..5];
+ let world = &s[6..11];
+```
+
+`hello`没有引用整个`String s`,而是引用了`s`的一部分内容,通过`[0..5]`的方式来指定。
+
+这就是创建切片的语法,使用方括号包括的一个序列: **[开始索引..终止索引]**,其中开始索引是切片中第一个元素的索引位置,而终止索引是最后一个元素后面的索引位置,也就是这是一个`右半开区间`。在内部,切片数据结构会保存开始的位置和切片的长度,其中长度是通过`终止索引` - `开始索引`的方式计算得来的。
+
+对于`let world = &s[6..11];`来说,`world`是一个切片,该切片的指针指向`s`的第7个字节(索引从0开始,6是第7个字节),且该切片的长度是`5`个字节。
+
+
+
+图:String切片引用了另一个`String`的一部分
+
+在使用Rust的`..`区间(range)语法时,如果你想从索引0开始,可以使用如下的方式,这两个是等效的:
+```rust
+let s = String::from("hello");
+
+let slice = &s[0..2];
+let slice = &s[..2];
+```
+
+同样的,如果你的切片想要包含`String`的最后一个字节,则可以这样使用:
+```rust
+let s = String::from("hello");
+
+let len = s.len();
+
+let slice = &s[3..len];
+let slice = &s[3..];
+```
+
+你也可以截取完整的`String`切片:
+```rust
+let s = String::from("hello");
+
+let len = s.len();
+
+let slice = &s[0..len];
+let slice = &s[..];
+```
+
+>在对字符串使用切片语法时需要格外小心,切片的索引必须落在字符之间的边界位置,也就是UTF8字符的边界,例如中文在UT8中占用三个字节,下面的代码就会崩溃:
+>```rust
+> let s = "中国人";
+> let a = &s[0..2];
+> println!("{}",a);
+>```
+>因为我们只取`s`字符串的前两个字节,但是一个中文占用三个字节,因此没有落在边界处,也就是连`中`字都取不完整,此时程序会直接崩溃退出,如果改成`&a[0..3]`,则可以正常通过编译.
+> 因此,当你需要对字符串做切片索引操作时,需要格外小心这一点, 关于该如何操作utf8字符串,参见[这里](#操作UTF8字符串)
+
+字符串切片的类型标示是`&str`,因此我们可以这样申明一个函数,输入`String`类型,返回它的切片: `fn first_word(s: &String) -> &str `.
+
+有了切片就可以写出这样的安全代码:
+```rust
+fn main() {
+ let mut s = String::from("hello world");
+
+ let word = first_word(&s);
+
+ s.clear(); // error!
+
+ println!("the first word is: {}", word);
+}
+```
+编译器报错如下:
+```console
+error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
+ --> src/main.rs:18:5
+ |
+16 | let word = first_word(&s);
+ | -- immutable borrow occurs here
+17 |
+18 | s.clear(); // error!
+ | ^^^^^^^^^ mutable borrow occurs here
+19 |
+20 | println!("the first word is: {}", word);
+ | ---- immutable borrow later used here
+```
+
+回忆一下借用的规则:当我们已经有了可变借用时,就无法再拥有不可变的借用。因为`clear`需要清空改变`String`,因此它需要一个可变借用,而之后的`println!`又使用了不可变借用,因此编译无法通过。
+
+从上述代码可以看出,Rust不仅让我们的`api`更加容易使用,而且也在编译器就位我们消除了大量错误!
+
+#### 其它切片
+因为切片是对集合的部分引用,因此不仅仅字符串有切片,其它集合类型也有,例如数组:
+```rust
+let a = [1, 2, 3, 4, 5];
+
+let slice = &a[1..3];
+
+assert_eq!(slice, &[2, 3]);
+```
+该数组切片的类型是`&[i32]`,数组切片和字符串切片的工作方式是一样的,例如持有一个引用指向原始数组的某个元素和长度。对于集合类型,我们在[这一章](../advance/collection.md)中有详细的介绍。
+
+
+## 字符串字面量是切片
+
+之前提到过字符串字面量,但是没有提到它的类型:
+```rust
+let s = "Hello, world!";
+```
+
+实际上,`s`的类型时`&str`,因此你也可以这样声明:
+```rust
+let s: &str = "Hello, world!";
+```
+该切片指向了程序可执行文件中的某个点,这也是为什么字符串字面量是不可变的,因为`&str`时一个不可变引用。
+
+了解完切片,可以进入本节的正题了。
+
+## 什么是字符串?
+
+顾名思义,字符串是由字符组成的连续集合,但是在上一节中我们提到过,**Rust中的字符是Unicode类型,因此每个字符占据4个字节内存空间,但是在字符串中不一样,字符串是UTF8编码,也就是字符所占的字节数是变长的(1-4)**,这样有助于大幅降低字符串所占用的内存空间.
+
+Rust在语言级别,只有一种字符串类型:`str`,它通常是以引用类型出现`&str`,也就是上文提到的字符串切片。虽然语言级别只有上述的`str`类型,但是在标准库里,还有多种不同用途的字符串类型,其中使用最广的即是`String`类型。
+
+`str`类型是硬编码进可执行文件,也无法被修改,但是`String`则是一个可增长、可改变且具有所有权的UTF8编码字符串,**当Rust用户提到字符串时,往往指的就是`String`类型和`&str`字符串切片类型,这两个类型都是UTF8编码**.
+
+除了`String`类型的字符串,Rust的标准库还提供了其他类型的字符串,例如`OsString`,`OsStr`,`CsString`和`CsStr`等,注意到这些名字都以`String`或者`Str`结尾了吗?它们分别对应的是具有所有权和被借用的变量。
+
+#### 操作字符串
+
+由于String是可变字符串,因此我们可以对它进行创建、增删操作,下面的代码汇总了相关的操作方式:
+```rust
+fn main() {
+ // 创建一个空String
+ let mut s = String::new();
+ // 将&str类型的"hello,world"添加到中
+ s.push_str("hello,world");
+ // 将字符'!'推入s中
+ s.push('!');
+ // 最后s的内容是"hello,world!"
+ assert_eq!(s,"hello,world!");
+
+ // 从现有的&str切片创建String类型
+ let mut s = "hello,world".to_string();
+ // 将字符'!'推入s中
+ s.push('!');
+ // 最后s的内容是"hello,world!"
+ assert_eq!(s,"hello,world!");
+
+ // 从现有的&str切片创建String类型
+ // String与&str都是UTF8编码,因此支持中文
+ let mut s = String::from("你好,世界");
+ // 将字符'!'推入s中
+ s.push('!');
+ // 最后s的内容是"hello,world!"
+ assert_eq!(s,"你好,世界!");
+
+ let s1 = String::from("Hello,");
+ let s2 = String::from("world!");
+ // 在下句中,s1的所有权被转移走了,因此后面不能再使用s1
+ let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
+ assert_eq!(s3,"hello,world!");
+ // 下面的语句如果去掉注释,就会报错
+ // println!("{}",s1);
+}
+```
+
+在上面代码中,有一处需要解释的地方,就是使用`+`来对字符串进行相加操作, 这里之所以使用`s1 + &s2`的形式,是因为`+`使用了`add`方法,该方法的定义类似:
+```rust
+fn add(self, s: &str) -> String {
+```
+
+因为该方法涉及到更复杂的特征功能,因此我们这里简单说明下,`self`是`String`类型的字符串`s1`,该函数说明,只能将&str类型的字符串切片添加到String类型的`s1`上,然后返回一个新的`String`类型,所以`let s3 = s1 + &s2;`就很好解释了,将`String`类型的`s1`与`&str`类型的`s2`进行相加,最终得到`String`类型的s3.
+
+由此可推,以下代码也是合法的:
+```rust
+ let s1 = String::from("tic");
+ let s2 = String::from("tac");
+ let s3 = String::from("toe");
+
+ // String = String + &str + &str + &str + &str
+ let s = s1 + "-" + &s2 + "-" + &s3;
+```
+
+`String` + `&str`返回一个`String`,然后再继续跟一个`&str`进行`+`操作,返回一个`String`类型,不断循环,最终生成一个`s`,也是`String`类型。
+
+在上面代码中,我们做了一个有些难以理解的`&String`操作,下面来展开讲讲。
+
+## String与&str的转换
+
+在之前的代码中,已经见到好几种从`&str`类型生成`String`类型的操作:
+- `String::from("hello,world")`
+- `"hello,world".to_string()`
+
+那么如何将`String`类型转为`&str`类型呢?答案很简单,取引用即可:
+```rust
+fn main() {
+ let s = String::from("hello,world!");
+ say_hello(&s);
+ say_hello(&s[..]);
+ say_hello(s.as_str());
+}
+
+fn say_hello(s: &str) {
+ println!("{}",s);
+}
+```
+
+实际上这种灵活用法是因为`deref`强制转换,具体我们会在[Deref特征](../traits/deref.md)进行详细讲解。
+
+## 字符串索引
+
+在其它语言中,使用索引的方式访问字符串的某个字符或者子串是很正常的行为,但是在Rust中就会报错:
+```rust
+ let s1 = String::from("hello");
+ let h = s1[0];
+```
+
+该代码会产生如下错误:
+```console
+3 | let h = s1[0];
+ | ^^^^^ `String` cannot be indexed by `{integer}`
+ |
+ = help: the trait `Index<{integer}>` is not implemented for `String`
+```
+https://rustwiki.org/en/book/ch08-02-strings.html#storing-utf-8-encoded-text-with-strings
+
+#### 深入字符串内部
+字符串的底层的数据存储格式实际上是[u8],一个字节数组。对于`let hello = String::from("Hola");`这行代码来说,`hello`的长度是`4`个字节,因为`"hola"`中的每个字母在UTF8编码中仅占用1个字节,但是对于下面的代码呢?
+```rust
+let hello = String::from("中国人");
+```
+如果问你该字符串多长,你可能会说`3`,但是实际上是`9`个字节的长度,因为每个汉字在UTF8中的长度是`3`个字节,因此这种情况下对`hello`进行索引
+访问`&hello[0]`没有任何意义,因为你取不到`中`这个字符,而是取到了这个字符三个字节中的第一个字节,这是一个非常奇怪而且难以理解的返回值。
+
+#### 字符串的不同表现形式
+现在看一下用梵文写的字符串`“नमस्ते”`, 它底层的字节数组如下形式:
+```rust
+[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,
+224, 165, 135]
+```
+长度是18个字节,这也是计算机最终存储该字符串的形式。如果从字符的形式去看,则是:
+```rust
+['न', 'म', 'स', '्', 'त', 'े']
+```
+但是这种形势下,第四和六两个字母根本就不存在,没有任何意义,接着再从字母串的形式去看:
+```rust
+["न", "म", "स्", "ते"]
+```
+
+所以,可以看出来Rust提供了不同的字符串展现方式,这样程序可以挑选自己想要的方式去使用,而无需去管字符串从人类语言角度看长什么样。
+
+还有一个原因导致了Rust不允许去索引字符:因为索引操作,我们总是期望它的性能表现是O(1),然后对于`String`类型来说,无法保证这一点,因为Rust可能需要从0开始去遍历字符串来定位合法的字符。
+
+## 字符串切片
+前文提到过,字符串切片是非常危险的操作,因为切片的索引是通过字节来进行,但是字符串是UTF8编码,因此你无法保证索引的字节刚好落在字符的边界上,例如:
+```rust
+let hello = "中国人";
+
+let s = &hello[0..2];
+```
+运行上面的程序,会直接造成崩溃:
+```console
+thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '中' (bytes 0..3) of `中国人`', src/main.rs:4:14
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+```
+这里提示的很清楚,我们索引的字节落在了`中`字符的内部,这种返回没有任何意义。
+
+因此在通过索引区间来访问字符串时,需要格外的小心,一不注意,就会导致你程序的崩溃!
+
+## 操作UTF8字符串
+前文提到了几中使用UTF8字符串的方式,下面来一一说明。
+
+#### 字符
+如果你想要以Unicode字符的方式遍历字符串,最好的办法是使用`chars`方法,例如:
+```rust
+for c in "中国人".chars() {
+ println!("{}", c);
+}
+```
+输出如下
+```console
+中
+国
+人
+```
+
+#### 字节
+这种方式是返回字符串的底层字节数组表现形式:
+```rust
+for b in "中国人".bytes() {
+ println!("{}", b);
+}
+```
+输出如下:
+```console
+228
+184
+173
+229
+155
+189
+228
+186
+186
+```
+
+#### 获取子串
+想要准确的从UTF8字符串中获取子串是较为复杂的事情,例如想要从`holla中国人नमस्ते`这种变长的字符串中取出某一个子串,使用标准库你是做不到的,
+你需要在`crates.io`上搜索`utf8`来寻找想要的功能。
+
+可以考虑尝试下这个库:[utf8 slice](https://crates.io/crates/utf8_slice).
+
+## String底层剖析
+@todo
\ No newline at end of file
diff --git a/src/basic/string.md b/src/basic/string.md
deleted file mode 100644
index d0366e68..00000000
--- a/src/basic/string.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# 字符串
-
-在其他语言,字符串往往是送分题,因为实在是太简单了,例如`"hello, world"`就是字符串章节的几乎全部内容了,对吧?如果你带着这样的想法来学Rust,
-我保证,绝对会栽跟头,**因此这一章大家一定要重视,仔细阅读,这里有很多其它Rust书籍中没有的内容**。
-
-首先来看段很简单的代码:
-```rust
-fn main() {
- let my_name = "Pascal";
- greet(my_name);
-}
-
-fn greet(name: String) {
- println!("Hello, {}!", name);
-}
-```
-
-`greet`函数接受一个字符串类型的`name`参数,然后打印到终端控制台中,非常好理解,你们猜猜,这段代码能否通过编译?
-
-```conole
-error[E0308]: mismatched types
- --> src/main.rs:3:11
- |
-3 | greet(my_name);
- | ^^^^^^^
- | |
- | expected struct `std::string::String`, found `&str`
- | help: try using a conversion method: `my_name.to_string()`
-
-error: aborting due to previous error
-```
-
-Bingo,果然报错了,编译器提示`greet`函数需要一个`String`类型的字符串,却传入了一个`&str`类型的字符串,相信读者心中现在一定有几头草泥马呼啸而过,怎么字符串也能整出这么多花活?
-
-接下来,让我们逐点分析讲解。
-
-## 什么是字符串?
-
-顾名思义,字符串是由字符组成的连续集合,但是在上一节中我们提到过,**Rust中的字符是Unicode类型,因此每个字符占据4个字节内存空间,但是在字符串中不一样,字符串是UTF8编码,也就是字符所占的字节数是变长的(2-4)**,这样有助于大幅降低字符串所占用的内存空间.
-
-Rust在语言级别,只有一中字符串类型:`str`,它通常是以引用类型(更准确的说法是[借用](../../core/borrowing.md),这个概念在后面会讲)出现`&str`,
-
-
-## String底层剖析
-
-https://rustwiki.org/zh-CN/book/ch04-01-what-is-ownership.html#变量与数据交互的方式一移动
-
-> 为何`String`长度和容量会不一致?
-> 之前提到过`String`是一个可修改、可增长的字符串,因此它是可变的,但是不可能在每次改变,我们都重新生成一次堆上的内存空间,这种成本太高了,因此
\ No newline at end of file
diff --git a/src/errors/panic-codes.md b/src/errors/panic-codes.md
index e717b592..3f585cf7 100644
--- a/src/errors/panic-codes.md
+++ b/src/errors/panic-codes.md
@@ -1,6 +1,9 @@
# 会导致panic的代码
+String slice range indices must occur at valid UTF-8 character boundaries. If you attempt to create a string slice in the middle of a multibyte character, your program will exit with an error. For the purposes of introducing string slices, we are assuming ASCII only in this section; a more thorough discussion of UTF-8 handling is in the “Storing UTF-8 Encoded Text with Strings” section of Chapter 8.
+
+
>
> 比方说有一个 `u8` ,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生**整型溢出**。关于这一行为 Rust 有一些有趣的规则。当在 debug 模式编译时,Rust 会检查整型溢出若存在这些问题则使程序在编译时 *panic*。Rust 使用这个术语来表明程序因错误而退出。 [该章节](../../errors/panic.md)会详细介绍 panic。
>
diff --git a/src/img/.DS_Store b/src/img/.DS_Store
index 0a8d907d..08567d11 100644
Binary files a/src/img/.DS_Store and b/src/img/.DS_Store differ
diff --git a/src/img/string-01.svg b/src/img/string-01.svg
new file mode 100644
index 00000000..e64415fe
--- /dev/null
+++ b/src/img/string-01.svg
@@ -0,0 +1,115 @@
+
+
+
+
+
diff --git a/src/traits/deref.md b/src/traits/deref.md
index 522c6414..65aa6344 100644
--- a/src/traits/deref.md
+++ b/src/traits/deref.md
@@ -1 +1,89 @@
# Deref和引用隐式转换
+
+`Deref` 是解引用操作符`*`的特征,比如 *v。
+
+一般理解,`*v`操作,是`&v`的反向操作,是为了获取`&v`指针指向的堆上对象。
+
+## 强制隐式转换
+
+Deref最神奇、最好用的地方并不在本身`解引`这个意义上,Rust的设计者在它之上附加了一个特性:强制隐式转换,这才是它神奇之处。
+
+这种隐式转换的规则为:
+
+一个类型为`T`的对象`foo`,如果T: Deref,那么,相关`foo`的引用`&foo`在应用的时候会自动转换`&U`。
+
+粗看这条规则,貌似有点类似于`AsRef`,而跟`解引`似乎风马牛不相及, 实际里面里面有些玄妙之处。
+
+Rust编译器会在做`*v`操作的时候,自动先把`v`做引用归一化操作,即转换成内部通用引用的形式`&v`,整个表达式就变成 `*&v`。这里面有两种情况:
+
+1. 把智能指针(比如在库中定义的,Box, Rc, Arc, Cow 等),去掉壳,转成内部标准形式`&v`;
+2. 把多重`&` (比如:`&&&&&&&v`),简化成`&v`(通过插入足够数量的`*`进行解引)。
+所以,它实际上在解引用之前做了一个引用的归一化操作。
+
+为什么要转呢? 因为编译器设计的能力是,只能够对 &v 这种引用进行解引用。其它形式的它不认识,所以要做引用归一化操作。
+
+使用引用进行过渡也是为了能够防止不必要的拷贝。
+
+下面举一些例子:
+```rust
+ fn foo(s: &str) {
+ // borrow a string for a second
+ }
+
+ // String implements Deref
+ let owned = "Hello".to_string();
+
+ // therefore, this works:
+ foo(&owned);
+```
+
+因为`String`实现了`Deref`。
+
+```rust
+ use std::rc::Rc;
+
+ fn foo(s: &str) {
+ // borrow a string for a second
+ }
+
+ // String implements Deref
+ let owned = "Hello".to_string();
+ let counted = Rc::new(owned);
+
+ // therefore, this works:
+ foo(&counted);
+```
+
+因为`Rc`实现了`Deref`。
+
+```rust
+ fn foo(s: &[i32]) {
+ // borrow a slice for a second
+ }
+
+ // Vec implements Deref
+ let owned = vec![1, 2, 3];
+
+ foo(&owned);
+```
+
+因为`Vec` 实现了`Deref`。
+
+```rust
+ struct Foo;
+
+ impl Foo {
+ fn foo(&self) { println!("Foo"); }
+ }
+
+ let f = &&Foo;
+
+ f.foo();
+ (&f).foo();
+ (&&f).foo();
+ (&&&&&&&&f).foo();
+```
+
+上面那几种函数的调用,效果是一样的。
+
+这种`Deref`涉及的隐式转换,实际上是Rust中仅有的类型隐式转换,设计它的目的,是为了简化程序的书写,让代码不至于过于繁琐。把人从无尽的类型细节中解脱出来,让书写 Rust 代码变成一件快乐的事情。
\ No newline at end of file