diff --git a/src/enums_and_pattern_matching/defining_an_enum.md b/src/enums_and_pattern_matching/defining_an_enum.md index 4600e3e..ac982ab 100644 --- a/src/enums_and_pattern_matching/defining_an_enum.md +++ b/src/enums_and_pattern_matching/defining_an_enum.md @@ -179,53 +179,52 @@ struct ChangeColorMessage(i32, i32, i32); // 元组结构体 ``` -但是,如果我们使用不同结构体(每个结构体都有自己的类型),我们就无法像使用清单 6-2 中定义的 `Message` 枚举(这是一种单一类型)那样,轻松定义出取任何一种这些类别消息。 +但是,如果我们使用不同结构体(每个结构体都有自己的类型),我们就无法像使用清单 6-2 中定义的 `Message` 枚举(是个单一类型)那样,轻松定义出取任何一种这些类别消息的函数。 + +枚举和结构体之间,还有个相似之处:正如我们可以使用 `impl` 在结构体上定义方法一样,我们也可以在枚举上定义方法。下面是我们可以在 `Message` 枚举上,定义的一个名为 `call` 的方法: -枚举与结构体之间,还有另外一个相似点:正如在结构体上使用 `impl` 关键字定义出一些方法,在枚举上定义方法也是可以的。下面就是一个可定义在这里的 `Message` 枚举上、名为 `call` 的方法: ```rust -enum Message { - Quit, - Move { x: i32, y: i32 }, - Write (String), - ChangeColor(i32, i32, i32), -} - impl Message { fn call(&self) { // 方法体将定义在这里 } } -fn main() { let m = Message::Write(String::from("hello")); m.call(); -} ``` -方法体将使用 `self`,来获取方法调用所在变种实例值。在此示例中,已创建了一个有着值 `Message::Write(String::from("hello"))` 的变量 `m`,而那就是在 `m.call()` 运行时,`call` 方法体中的那个 `self`。 -下面就来看看标准库中另一个甚为常见和有用的枚举:`Option`。 +这个方法的主体,将使用 `self` 来获取我们在其上调用该方法的值。在本例中,我们创建了一个值为 `Message::Write(String::from("hello"))` 的变量 `m`,当 `m.call()` 运行时,这就是出现在 `call` 方法主体中 `self` 的内容。 + +我们来看看标准库中,另一个非常常见和有用的枚举:`Option`。 -## 枚举 `Option` 及其超越空值的诸多优点 +## `Option` 枚举及其相对于空值的优势 **The `Option` Enum and Its Advantages Over Null Values** -本小节会探讨 `Option` 的案例研究,`Option` 是由标准库定义的另一个枚举。`Option` 类型编码了某个值可能会是某个物件,或可能什么都不属于的这种甚为常见的场景(the `Option` type encodes the very common scenario in which a value could be something or it could be nothing)。比如在请求某个含有一些项目的清单的第一个项目时,就会得到一个值。而在请求某个空清单的第一个项目时,则会什么也得不到。以类型系统字眼,来表达这个概念,就表示编译器能够对,是否已处理了本应处理的全部情形,进行检查;此项功能可阻止那些在其他编程语言中极为常见的代码错误。 -编程语言的设计,通常要考量包含哪些特性,而要排除哪些特性也至关重要。Rust 没有许多其他语言都有的空值特性。*空值(Null)* 是一个表示此处无值的值。在带有 `null` 的那些语言中,变量总是会处于下面两个状态之一:空值或非空值。 +本节会探讨 `Option` 的一个案例研究,这是标准库定义的另一枚举。`Option` 类型编码了一种非常常见的情况,即某个值可能是某物,也可能什么都不是。 -在 `null` 的发明人Tony Hoare 于 2009 年的演讲 “空值引用:10 亿美金代价失误(Null Reference: The Billion Dollar Mistake)” 中,就讲了这个问题: +例如,如果咱们请求了某个非空列表中的首个项目,咱们将得到某个值。如果咱们请求某个空列表中的第一项,则什么也得不到。用类型系统来表达这一概念,意味着编译器可以检查,咱们是否处理了所有应处理的情况;这一功能可以防止其他编程语言中,极为常见的编程错误。 -> 我把他叫做我的 10 亿美元失误。那个时候我正在设计某门面向对象语言中的首个综合类型系统。目的是要在编译器自动执行的检查之下,确保全部引用使用,都应绝对安全。仅仅因为空值引用变量实现起来很容易,我当时就没能顶住诱惑,把他加入到特性集了。这个举动,业已造成了数不胜数的代码错误、漏洞及系统崩溃等等问题,在过去 40 余年里,这些问题可能已经造成大概 10 亿美金的痛苦和伤害。 +编程语言的设计,通常会考虑包含哪些功能,但排除哪些功能,也很重要。Rust 没有许多其他语言所具有的空值 null 功能。*Null* 是个表示没有值的值。在有控制的语言中,变量总是处于两种状态之一:空值或非空值。 -`null` 值的问题在于,当尝试将 `null` 值用作非 `null` 值时,就会得到某种错误。由于这种 `null` 或非 `null` 的属性遍布各处,因此极容易犯下此类错误。 +空值的发明者 Tony Hoare 于 2009 年的演讲 “空值引用:10 亿美金代价失误(Null Reference: The Billion Dollar Mistake)” 中,就讲了这个问题: -但 `null` 试图表达的概念,还是有用的:`null` 是个因某些原因,而当前为无效或空缺的值。 -问题不是真的在于这个概念,而在于针对性的实现。由于这些原因,Rust 就没有空值,但他确实有一个可对值存在或空缺这个概念,进行编码的枚举。这个枚举就是 `Option`,而这个枚举 [由标准库定义](https://doc.rust-lang.org/std/option/enum.Option.html) 为下面这样: +> 我称他为我的 "十亿美元错误"。当时,我正在为面向对象语言中的引用,设计第一个全面的类型系统。我的目标是确保所有引用的使用,都绝对安全,并由编译器自动进行检查。但是,我无法抵制加入空引用的诱惑,只是因为这太容易实现了。这导致了无数的错误、漏洞和系统崩溃,在过去的四十年里,这些错误和漏洞可能造成了数十亿美元的损失。 + + +空值的问题在于,如果试图将空值用作非空值,就会出现某种错误。由于这种空值或非空值属性普遍存在,因此极易出现这种错误。 + +然而,空值试图表达的这种概念,仍然是有用的:空值是指由于某种原因,当前无效或不存在的值。 + +问题其实不在于概念,而在于特定的实现。因此,Rust 没有空值,但有个枚举可以编码值存在或不存在的概念。这个枚举就是 `Option`,[标准库对其定义](https://doc.rust-lang.org/std/option/enum.Option.html) 如下: + ```rust enum Option { @@ -234,7 +233,8 @@ enum Option { } ``` -这个 `Option` 是如此重要,以至于在 Rust 序曲(the prelude)中甚至都包含了;是不需要显式地将其带入到作用域的(注:*原生类型、这里的 `Option`,以及前面的 `String` 类型等等,就是这样的包含在序曲中的类型,无需显式地带入到作用域,就可以直接使用*)。该枚举的变种也已包含在 Rust 序曲中:可直接在不带前缀 `Option::` 的情况下直接使用 `Some` 与 `None`(注:*那么 `Some` 与 `None` 就被列为了 Rust 关键字了*)。`Option` 仍然只是常规枚举,而 `Some` 与 `None` 仍然是类型 `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` 来保存数字与字符串类型的一些示例: