From 55746ebe6aa0eb4956b4aec1b87aceeb95bded38 Mon Sep 17 00:00:00 2001 From: "rust-lang.xfoss.com" Date: Mon, 18 Dec 2023 13:17:33 +0800 Subject: [PATCH] Refining Ch06. --- projects/option_demo/Cargo.toml | 8 +++ projects/option_demo/src/main.rs | 6 +++ .../defining_an_enum.md | 51 +++++++++++-------- 3 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 projects/option_demo/Cargo.toml create mode 100644 projects/option_demo/src/main.rs diff --git a/projects/option_demo/Cargo.toml b/projects/option_demo/Cargo.toml new file mode 100644 index 0000000..9026488 --- /dev/null +++ b/projects/option_demo/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "option_demo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/projects/option_demo/src/main.rs b/projects/option_demo/src/main.rs new file mode 100644 index 0000000..d6b8031 --- /dev/null +++ b/projects/option_demo/src/main.rs @@ -0,0 +1,6 @@ +fn main() { + let x: i8 = 5; + let y: Option = Some(5); + + let sum = x + y; +} diff --git a/src/enums_and_pattern_matching/defining_an_enum.md b/src/enums_and_pattern_matching/defining_an_enum.md index ac982ab..e92579c 100644 --- a/src/enums_and_pattern_matching/defining_an_enum.md +++ b/src/enums_and_pattern_matching/defining_an_enum.md @@ -236,20 +236,23 @@ enum Option { `Option` 枚举非常有用,以致他甚至被包含在 Rust 前奏中;咱们不需要显式地将他引入作用域。他的变种也包含在前奏中:咱们可以直接使用 `Some` 和 `None`,而无需 `Option::` 这个前缀。`Option` 枚举仍然只是个普通的枚举,而 `Some(T)` 和 `None`,也仍然是 `Option` 类型的变种。 -这里的 `` 语法,是个到目前为止还未讲到的 Rust 特性。他是个泛型参数,而在第 10 章将更详细的涉及到泛型。至于现在,只需明白 `` 表示 `Option` 枚举的 `Some` 变种,可保存任意类型的一条数据,而在 `T` 位置处用到的各个具体类型,会让整个 `Option` 类型成为各异的类型(for now, all you need to know is that `` means the `Some` variant of the `Option` enum can hold one piece of data of any type, and that each concrete type that gets used in place of `T` makes the overall `Option` type a different type)。以下是使用 `Option` 来保存数字与字符串类型的一些示例: +`` 这种语法,是我们尚未讨论过的一项 Rust 特性。他是个泛型参数,我们将在第 10 章,详细介绍泛型。现在,咱们只需知道 `` 表示 `Option` 枚举的 `Some` 变种,可以容纳任意类型的数据,而每个用来代替 `T` 的具体类型,都会使整个 `Option` 类型,成为不同的类型。下面是一些使用 `Option` 值,保存数字类型和字符串类型的示例: + ```rust let some_numer = Some(5); - let some_string = Some("一个字符串"); + let some_char = Some('e'); let absent_number: Option = None; ``` -`some_number` 的类型为 `Option`。`some_string` 的类型为 `Option<&str>`,是个不同的类型。由于这里已在 `Some` 变种里面指定了值,因此 Rust 可推导出这些类型来。而对于 `absent_number`,Rust 就要求注释整个 `Option` 类型:编译器无法通过仅查看一个 `None` 值,而推导出相应的 `Some` 变种的类型来。这里告诉了 Rust,这里计划的是 `absent_number` 为类型 `Option`。 -在有着一个 `Some` 值时,就知道存在着一个值,且该值是保存在 `Some` 内部的。而在有个 `None` 值时,某种意义上讲,这表示了与空值同样的情况:没有一个有效值。那么究竟为什么有着 `Option` 就是要比有着空值 `null` 好呢? +其中 `some_number` 的类型是 `Option`。`some_char` 的类型是 `Option`,这是一种不同的类型。Rust 可以推断出这些类型,因为我们在 `Some` 变种中,指定了某个值。对于 `absent_number`,Rust 要求我们注解整个 `Option` 类型:编译器无法仅通过查看 `None` 值,来推断相应 `Some` 变种将持有的类型。在这里,我们告诉 Rust,我们的意思是 `absent_number` 属于 `Option` 类型。 + +当我们有某个 `Some` 值时,我们知道有个值存在,并且该值被保存在 `Some` 中。当我们有个 `None` 值时,从某种意义上说,他的含义与空值相同:我们没有一个有效值。那么,为什么 `Option` 比空值更好呢? + +简而言之,由于 `Option` 和 `T`(`T` 可以是任何类型)属于不同的类型,编译器不会让我们,将某个 `Option` 值用作其肯定是个有效值。例如,下面这段代码将不会编译,因为他试图将一个 `i8`,与一个 `Option` 相加: -简而言之,由于 `Option` 和 `T` (其中的 `T` 可以是任意类型) 为不同类型,因此编译器就不会允许将一个 `Option` 值,当作一个必然的有效值来使用。比如,由于下面这段代码是在尝试将一个 `i8` 值,添加到某个 `Option` 上,因此这段代码不会编译: ```rust let x: i8 = 5; @@ -258,29 +261,37 @@ enum Option { let sum = x + y; ``` -在运行这段代码时,就会得到下面这样的错误消息: + +如果我们运行这段代码,我们会收到如下报错: + ```console -$ cargo run  ✔ - Compiling enum_demo v0.1.0 (/home/peng/rust-lang/projects/enum_demo) +$ cargo run + Compiling option_demo v0.1.0 (C:\tools\msys64\home\Lenny.Peng\rust-lang-zh_CN\projects\option_demo) error[E0277]: cannot add `Option` to `i8` - --> src/main.rs:24:17 - | -24 | let sum = x + y; - | ^ no implementation for `i8 + Option` - | - = help: the trait `Add>` is not implemented for `i8` + --> src\main.rs:5:17 + | +5 | let sum = x + y; + | ^ no implementation for `i8 + Option` + | + = help: the trait `Add>` is not implemented for `i8` + = help: the following other types implement trait `Add`: + + > + <&'a i8 as Add> + <&i8 as Add<&i8>> For more information about this error, try `rustc --explain E0277`. -error: could not compile `enum_demo` due to previous error +error: could not compile `option_demo` (bin "option_demo") due to previous error ``` -强悍!事实上,这个错误消息表示,由于 `i8` 与 `Option` 属于不同类型,因此 Rust 不知道怎样将一个 `i8` 值与一个 `Option` 值相加。当在 Rust 中有着一个类型好比 `i8` 这样的值时,编译器就会保证始终有个有效值。在对那个值进行使用前,可不必检查他是不是 `null`,而可放心地加以处理。仅当有个 `Option` 类型(或其他任何正在使用的`Option` 枚举类型值)的变量时,才真地必须关心可能并无值,同时编译器将确保在使用该值前,显式地处理无值的情况。 -也就是说,在对 `Option` 类型转换为 `T` 类型。通常,这样做有助于捕获到 `null` 最常见问题之一:在某个东西实际上是 `null` 时,错误地将其设想为了他不是 `null`。 +强悍如此!实际上,这条报错信息,表示 Rust 不理解如何将某个 `i8` 与某个 `Option` 相加,因为他们属于不同的类型。在 Rust 中,当我们有个 `i8` 类型的值时,编译器会确保我们,始终有个有效的值。在使用该值之前,我们无需检查是否为空值,就可以放心地继续。只有当我们有个 `Option`(或其他任何类型的值)时,我们才必须担心可能并没有值,编译器会确保我们,在使用该值前处理这种情况。 -这种消除了不正确的假定某个非 `null` 值的做法,有助于增强代码自信。为了使用一个可能为 `null` 的值,就必须显式地通过将那个值的构造为 `Option`,来带入这个值。在某个类型不为 `Option` 值出现的每个地方,就都可以假定该值不是 `null`。这是 Rust 有意的设计决定,用以限制 `null` 的无处不在,及提升 Rust 代码的安全性。 +换句话说,在对 `Option` 执行 `T` 的运算之前,我们必须先将其转换为 `T`。一般来说,这有助于捕捉空值最常见的问题之一:假设了某个项目不是空值,而实际上他却是空值。 -那么在有一个类型为 `Option` 值的时候,该怎么从 `Some` 变种获取到 `T` 这个值,从而就可以用上那个值呢?枚举 `Option` 有着大量的、在不同情形下有用的方法;在 [`Option` 文档](https://doc.rust-lang.org/std/option/enum.Option.html) 便可查看到这些方法。熟悉 `Option` 上的这些方法,将对 Rust 编程生涯极为有用。 +消除错误地假定某个非空值的风险,可以让咱们对咱们代码更有信心。为了获得某个可能为空的值,咱们必须显式地选择,将该值的类型设为 `Option`。然后,在使用该值时,咱们必须显式地处理,该值为空的情况。只要值的类型不是 `Option`,咱们就可以放心地认为,该值不是空值。这是 Rust 为限制空值泛滥,和提高 Rust 代码的安全性,而特意做出的设计决定。 -总的来说,为了使用某个 `Option` 值,就要有将会处理各个变种的代码。要有一些只会在有着一个 `Some` 的值时运行的代码,而此情况下就会允许这代码使用那个内部的 `T` 类型变量。在有着 `None` 值时,则还要有别的代码来允许了,而这代码就没有可用的 `T` 类型值了。在与枚举一起使用的时候,`match` 表达式正是实现此特性的控制流结构:`match` 表达式将依据枚举有着哪些变种,而运行相应的不同代码,以及哪些代码可使用匹配值内部的数据。 +那么,当咱们有着某个 `Option` 类型的值时,该怎样从 `Some` 变种中获取到那个 `T` 值,以便使用该值呢?`Option` 这个枚举,有着大量在不同场景下,都有用的方法;咱们可以在 [其文档](https://doc.rust-lang.org/std/option/enum.Option.html) 中,查看这些方法。熟悉 `Option` 的方法,将对咱们的 Rust 之旅大有裨益。 + +一般来说,为了使用某个 `Option` 值,咱们需要编写处理每个变种的代码。咱们会想要一些,仅在咱们有个 `Some(T)` 值时才会运行的代码,而这些代码,就可以使用内部的 `T` 值;咱们会想要另一些,只有在咱们有个 `None` 值时才会运行的代码,而这些代码就没有可用的 `T` 值。与枚举一起使用的 `match` 表达式,便是一种正好完成这个目的的控制流结构:他会根据枚举有着哪一个变种,而运行不同的代码,而这些代码,就可以使用匹配值内部的数据。