From 35db8fd78f66362130d72e5ff3d156639c6dfe7f Mon Sep 17 00:00:00 2001 From: sunface Date: Thu, 17 Feb 2022 17:32:26 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E5=88=87=E7=89=87=E4=B8=8E=E5=88=87?= =?UTF-8?q?=E7=89=87=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contents/SUMMARY.md | 1 + contents/confonding/intro.md | 3 ++ contents/confonding/slice.md | 57 +++++++++++++++++++++++++++++++++++ contents/confonding/string.md | 33 +++++++++++++++++++- 4 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 contents/confonding/slice.md diff --git a/contents/SUMMARY.md b/contents/SUMMARY.md index 85ebcad7..4f426c35 100644 --- a/contents/SUMMARY.md +++ b/contents/SUMMARY.md @@ -125,6 +125,7 @@ - [异步跟同步共存](tokio/bridging-with-sync.md) - [易混淆概念解析](confonding/intro.md) + - [切片和切片引用](confonding/slice.md) - [String、&str 和 str](confonding/string.md) - [原生指针、引用和智能指针 todo](confonding/pointer.md) - [作用域、生命周期和 NLL todo](confonding/lifetime.md) diff --git a/contents/confonding/intro.md b/contents/confonding/intro.md index 070164e7..83ce2926 100644 --- a/contents/confonding/intro.md +++ b/contents/confonding/intro.md @@ -1,2 +1,5 @@ # 易混淆概念解析 +Rust 之所以难,有部分原因在于一些概念对于刚入门的同学来说不仅难以理解,还容易混淆。 + +对于难以理解这一点,我们在之前的章节已经讲解的差不多。本章就来看看,那些容易混淆的概念该如何进行区分,例如 `String`、`str` 和 `&str`。 diff --git a/contents/confonding/slice.md b/contents/confonding/slice.md new file mode 100644 index 00000000..c6c464a1 --- /dev/null +++ b/contents/confonding/slice.md @@ -0,0 +1,57 @@ +# 切片和切片引用 +关于 `str` / `&str`,`[u8]` / `&[u8]` 区别,你能清晰的说出来嘛?如果答案是 No ,那就跟随我一起来看看切片和切片引用到底有何区别吧。 + +> 在继续之前,参见[此处](https://course.rs/basic/compound-type/string-slice.html#切片slice)了解何为切片 + +切片允许我们引用集合中部分连续的元素序列,而不是引用整个集合。例如,字符串切片就是一个子字符串,数组切片就是一个子数组。 + +## 无法被直接使用的切片类型 +Rust 语言特性内置的 `str` 和 `[u8]` 类型都是切片,前者是字符串切片,后者是数组切片,下面我们来尝试下使用 `str` : +```rust +let string: str = "banana"; +``` + +上面代码创建一个 `str` 类型的字符串,看起来很正常,但是编译就会报错: +```shell +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> src/main.rs:4:9 + | +4 | let string: str = "banana"; + | ^^^^^^ doesn't have a size known at compile-time +``` + +编译器准确的告诉了我们原因:`str` 字符串切片它是 [`DST` 动态大小类型](https://course.rs/advance/custom-type.html#动态大小类型),这意味着编译器无法在编译期知道 `str` 类型的大小,只有到了运行期才能动态获知,这对于强类型、强安全的 Rust 语言来说是不可接受的。 + +也就是说,我们无法直接使用 `str`,而对于 `[u8]` 也是类似的,大家可以自己动手试试。 + +总之,我们可以总结出一个结论:**在 Rust 中,所有的切片都是动态类型,它们都无法直接被使用**。 + +那么问题来了,该如何使用切片呢? + +## 切片引用 +何以解忧,唯有引用。由于引用类型的大小在编译期是已知的,因此在 Rust 中,如果要使用切片,就必须要使用它的引用。 + +`str` 切片的引用类型是 `&str`,而 `[i32]` 的引用类型是 `&[i32]`,相信聪明的读者已经看出来了,`&str` 和 `&[i32]` 都是我们非常常用的类型,例如: +```rust +let s1: &str = "banana"; +let s2: &str = &String::from("banana"); + +let arr = [1, 2, 3, 4, 5]; + +let s3: &[i32] = &arr[1..3]; +``` + +这段代码就可以正常通过,原因在于这些切片引用的大小在编译器都是已知的。 + +## 总结 +我们常常说使用切片,实际上我们在用的是切片的引用,我们也在频繁说使用字符串,实际上我们在使用的也是字符串切片的引用。 + +总之,切片在 Rust 中是动态类型 DST,是无法被我们直接使用的,而我们在使用的都是且切片的引用。 + +| 切片 | 切片引用| +| --- | --- | +| str 字符串切片 | &str 字符串切片的引用 | +| [u8] 数组切片| &[u8] 数组切片的引用 | + + +**但是出于方便,我们往往不会说使用切片引用,而是直接说使用字符串切片或数组切片,实际上,这时指代的都是切片的引用!** \ No newline at end of file diff --git a/contents/confonding/string.md b/contents/confonding/string.md index 229c05d9..94dd2bfb 100644 --- a/contents/confonding/string.md +++ b/contents/confonding/string.md @@ -1,4 +1,35 @@ -# String、&str 和 str todo +# 疯狂字符串 +字符串让人疯狂,这句话用在 Rust 中一点都不夸张,不信?那你能否清晰的说出 `String`、`str`、`&str`、`&String`、`Box` 或 `Box<&str>` 的区别? + +Rust 语言的类型可以大致分为两种:基本类型和标准库类型,前者是由语言特性直接提供的,而后者是在标准库中定义。即将登场的 `str` 类型就是唯一定义在语言特性中的字符串。 + +> 在继续之前,大家需要先了解字符串的[基本知识](https://course.rs/basic/compound-type/string-slice.html),本文主要在于概念对比,而不是字符串讲解 + +## str +如上所述,`str` 是唯一定义在 Rust 语言特性中的字符串,但是也是我们几乎不会用到的字符串类型,为何? + +原因在于 `str` 字符串它是 [`DST` 动态大小类型](https://course.rs/advance/custom-type.html#动态大小类型),这意味着编译器无法在编译期知道 `str` 类型的大小,只有到了运行期才能动态获知,这对于强类型、强安全的 Rust 语言来说是不可接受的。 + +```rust +let string: str = "banana"; +``` + +上面代码创建一个 `str` 类型的字符串,看起来很正常,但是编译就会报错: +```shell +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> src/main.rs:4:9 + | +4 | let string: str = "banana"; + | ^^^^^^ doesn't have a size known at compile-time +``` + + +同时还是 String 和 &str 的底层数据类型。 由于 str 是动态 + +`str` 类型是硬编码进可执行文件,也无法被修改,但是 `String` 则是一个可增长、可改变且具有所有权的 UTF8 编码字符串,**当 Rust 用户提到字符串时,往往指的就是 `String` 类型和 `&str` 字符串切片类型,这两个类型都是 UTF8 编码**。 + +除了 `String` 类型的字符串,Rust 的标准库还提供了其他类型的字符串,例如 `OsString`, `OsStr`, `CsString` 和` CsStr` 等,注意到这些名字都以 `String` 或者 `Str` 结尾了吗?它们分别对应的是具有所有权和被借用的变量。 +An str defines a slice of a block of data in memory which are meant to be interpreted as a sequence of characters and that’s all. It is uncertain where it is stored and how long that slice would be. This is why we can not use this type in a plain way in the time of writing. (Rust 1.58) https://pic1.zhimg.com/80/v2-177bce575bfaf289ae12d677689a26f4_1440w.png https://pic2.zhimg.com/80/v2-697ad53cb502ccec4b2e98c40975344f_1440w.png