From c8616ede1a8612a6fc7c5e979db5c1e30fc7dc41 Mon Sep 17 00:00:00 2001 From: AmaiKinono Date: Sat, 10 Mar 2018 22:37:42 +0800 Subject: [PATCH 1/5] fixed chapter 1. --- src/hello.md | 17 +++--- src/hello/comment.md | 23 ++++---- src/hello/print.md | 56 ++++++++++-------- src/hello/print/fmt.md | 27 +++++---- src/hello/print/print_debug.md | 48 +++++++++++---- src/hello/print/print_display.md | 59 ++++++++++--------- .../print/print_display/testcase_list.md | 41 ++++++++----- 7 files changed, 157 insertions(+), 114 deletions(-) diff --git a/src/hello.md b/src/hello.md index 89c5fcb7..84ab6f05 100644 --- a/src/hello.md +++ b/src/hello.md @@ -4,30 +4,30 @@ ```rust,editable // 这是注释内容,将会被编译器忽略掉 -// 可以单击前面的按钮 "Run" 来测试这段代码 -> -// 若想用键盘操作,可以使用快捷键"Ctrl + Enter"来运行 +// 可以单击那边的按钮 "Run" 来测试这段代码 -> +// 若想用键盘操作,可以使用快捷键 "Ctrl + Enter" 来运行 -// 这段代码支持编辑,你可以自由地改进代码! +// 这段代码支持编辑,你可以自由地修改代码! // 通过单击 "Reset" 按钮可以使代码恢复到初始状态 -> // 这是主函数 fn main() { - // 调用已编译成的可执行文件时,在这里面的语句将会运行 + // 调用编译生成的可执行文件时,这里的语句将被运行。 // 将文本打印到控制台 println!("Hello World!"); } ``` -`println!` 是一个 [**宏**][macros](macros),可以将文本输出到控制台(console)。 +`println!` 是一个[**宏**][macros](macro),可以将文本输出到控制台(console)。 -使用 Rust 的编译器 `rustc` 可以将源程序生成可执行文件: +使用 Rust 的编译器 `rustc` 可以从源程序生成可执行文件: ```bash $ rustc hello.rs ``` -`rustc` 编译后将得到可执行文件 `hello`。 +使用 `rustc` 编译后将得到可执行文件 `hello`。 ```bash $ ./hello @@ -36,7 +36,8 @@ Hello World! ### 动手试一试 -单击上面的 'Run' 按钮并观察输出结果。然后增加一行代码,再一次使用宏 `println!` 得到下面结果: +单击上面的 "Run" 按钮并观察输出结果。然后增加一行代码,再一次使用宏 `println!`, +得到下面结果: ```text Hello World! diff --git a/src/hello/comment.md b/src/hello/comment.md index 6ba338dc..d390b59e 100644 --- a/src/hello/comment.md +++ b/src/hello/comment.md @@ -2,35 +2,34 @@ 注释对任何程序都不可缺少,同样 Rust 支持几种不同的注释方式。 -* **普通注释**,其注释内容将被编译器忽略掉: +* **普通注释**,其内容将被编译器忽略掉: - `// 单行注释,注释内容直到行尾。 ` - `/* 块注释, 注释内容一直到结束分隔符。 */` -* **文档注释**,其注释内容将被解析成 HTML 帮助[文档][docs]: - - `/// 对接下来的项生成帮助文档。` - - `//! 对封闭项生成帮助文档。` +* **文档注释**,其内容将被解析成 HTML 帮助[文档][docs]: + - `/// 为接下来的项生成帮助文档。` + - `//! 为注释所属于的项(译注:如 crate、模块或函数)生成帮助文档。` ```rust,editable fn main() { // 这是行注释的例子 - // 注意这里有两个斜线在本行的开头 - // 在这里面的所有内容编译器都不会读取 + // 注意有两个斜线在本行的开头 + // 在这里面的所有内容都不会被编译器读取 // println!("Hello, world!"); - // 想要运行上述语句?现在请将上述语句的两条斜线删掉,并重新运行。 + // 请运行一下,你看到结果了吗?现在请将上述语句的两条斜线删掉,并重新运行。 /* - * 这是另外一种格式的注释——块注释。一般而言,行注释是推荐的注释格式, + * 这是另外一种注释——块注释。一般而言,行注释是推荐的注释格式, * 不过块注释在临时注释大块代码特别有用。/* 块注释可以 /* 嵌套, */ */ - * 所以只需很少按键就可注释掉这些在 main() 函数中的行。/*/*/* 赶紧试试!*/*/*/ + * 所以只需很少按键就可注释掉这些 main() 函数中的行。/*/*/* 自己试试!*/*/*/ */ /* - 注意,上面的例子中纵向都有 `*`,这完全是基于格式考虑,实际上这并不是 - 必须的。 + 注意,上面的例子中纵向都有 `*`,这只是一种风格,实际上这并不是必须的。 */ - // 观察块注释是如何对简单的表达式进行控制,而行注释不能这样操作。 + // 观察块注释是如何简单地对表达式进行修改的,行注释则不能这样。 // 删除注释分隔符将会改变结果。 let x = 5 + /* 90 + */ 5; println!("Is `x` 10 or 100? x = {}", x); diff --git a/src/hello/print.md b/src/hello/print.md index 1dfdac86..6716463b 100644 --- a/src/hello/print.md +++ b/src/hello/print.md @@ -1,24 +1,27 @@ # 格式化输出 -打印操作由[`std::fmt`][fmt]里面所定义的一系列[`宏`][macros]来处理,其中包括: +打印操作由 [`std::fmt`][fmt] 里面所定义的一系列[`宏`][macros]来处理,包括: -* `format!`:将格式化文本写到[`字符串`][string](String)。(译注: `字符串`是返回值不是参数。) -* `print!`:与 `format!`类似,但将文本输出到控制台。 -* `println!`: 与 `print!`类似,但输出结果追加一个换行符。 +* `format!`:将格式化文本写到[`字符串`][string](String)。(译注:`字符串`是返 +回值不是参数。) +* `print!`:与 `format!` 类似,但将文本输出到控制台(io::stdout)。 +* `println!`: 与 `print!` 类似,但输出结果追加一个换行符。 +* `eprint!`:与 `format!` 类似,但将文本输出到标准错误(io::stderr)。 +* `eprintln!`:与 `eprint!` 类似,但输出结果追加一个换行符。 -所有的解析文本都以相同的方式进行。另外一点是格式化的正确性在编译时检查。 +这些宏都以相同的做法解析文本。另外有个优点是格式化的正确性会在编译时检查。 ```rust,editable,ignore,mdbook-runnable fn main() { - // 通常情况下, `{}` 会被任意变量内容所替换。 - // 值内容会转化成字符串。 + // 通常情况下,`{}` 会被任意变量内容所替换。 + // 变量内容会转化成字符串。 println!("{} days", 31); - // 不加后缀的话,31自动成为 I32 类型。 - // 你可以添加后缀来改变 31 的原来类型。 + // 不加后缀的话,31 就自动成为 i32 类型。 + // 你可以添加后缀来改变 31 的类型。 - // 下面有多种可选形式。 - // 可以使用的位置参数。 + // 用变量替换字符串有多种写法。 + // 比如可以使用位置参数。 println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); // 可以使用赋值语句。 @@ -27,48 +30,51 @@ fn main() { subject="the quick brown fox", verb="jumps over"); - // 特殊的格式实现可以在后面加上 `:` 符号。 + // 可以在 `:` 后面指定特殊的格式。 println!("{} of {:b} people know binary, the other half don't", 1, 2); // 你可以按指定宽度来右对齐文本。 - // 下面语句输出" 1",5个空格后面连着1。 + // 下面语句输出 " 1",5 个空格后面连着 1。 println!("{number:>width$}", number=1, width=6); - // 你可以对数字左边位数上补0。下面语句输出"000001"。 + // 你可以在数字左边补 0。下面语句输出 "000001"。 println!("{number:>0width$}", number=1, width=6); // println! 会检查使用到的参数数量是否正确。 println!("My name is {0}, {1} {0}", "Bond"); - // 改正 ^ 补上漏掉的参数: "James" + // 改正 ^ 补上漏掉的参数:"James" - // 创建一个包含` I32 `类型结构体(structure)。命名为 `Structure`。 + // 创建一个包含单个 `i32` 的结构体(structure)。命名为 `Structure`。 #[allow(dead_code)] struct Structure(i32); - // 但是像结构体这样自定义类型需要更复杂的方式来处理。 + // 但是像结构体这样的自定义类型需要更复杂的方式来处理。 // 下面语句无法运行。 println!("This struct `{}` won't print...", Structure(3)); // 改正 ^ 注释掉此行。 } ``` -[`std::fmt`][fmt]包含多种[`traits`][traits](traits翻译成中文有“特征,特性”等意思)来控制文字显示。这里面有两个重要的基本格式类型如下: +[`std::fmt`][fmt] 包含多种 [`traits`][traits](trait 有 “特征,特性” 等意思) +来控制文字显示,其中重要的两种 trait 的基本形式如下: -* `fmt::Debug`:使用 `{:?}` 作标记。格式化文本以便调试。 -* `fmt::Display`:使用 `{}` 作标记。以优雅和友好的方式来格式文本。 +* `fmt::Debug`:使用 `{:?}` 标记。格式化文本以供调试使用。 +* `fmt::Display`:使用 `{}` 标记。以更优雅和友好的风格来格式文本。 -在本书中我们使用`fmt::Display`,因为标准库提供了这些类型的实现。若要打印自定义类型的文本,需要更多的步骤。 +上例使用了 `fmt::Display`,因为标准库提供了那些类型的实现。若要打印自定义类型的 +文本,需要更多的步骤。 ### 动手试一试 - * 改正上面代码中的两个错误(见 改正),使得运行不会报错。 + * 改正上面代码中的两个错误(见 “改正”),使它可以没有错误地运行。 - * 添加一个 `println!` 宏来打印:`Pi is roughly 3.142`(Pi约等于3.142),通过控制有效数字得到显示的结果。为了达到练习目的,使用 `let pi = 3.141592` 作为 Pi 的近似值(提示:设置小数位的显示格式可以参考文档[`std::fmt`][fmt])。 + * 再用一个 `println!` 宏,通过控制显示的小数位数来打印:`Pi is roughly 3.142` + (Pi 约等于 3.142)。为了达到练习目的,使用 `let pi = 3.141592` 作为 Pi 的近似 + 值(提示:设置小数位的显示格式可以参考文档 [`std::fmt`][fmt])。 ### 参见: -[`std::fmt`][fmt], [`macros`][macros], [`struct`][structs] -和 [`traits`][traits] +[`std::fmt`][fmt], [`macros`][macros], [`struct`][structs] 和 [`traits`][traits] [fmt]: http://doc.rust-lang.org/std/fmt/ [macros]: ./macros.html diff --git a/src/hello/print/fmt.md b/src/hello/print/fmt.md index 406202b0..c8b43a8c 100644 --- a/src/hello/print/fmt.md +++ b/src/hello/print/fmt.md @@ -1,16 +1,17 @@ # 格式化 -我们可以看到格式化就是通过**格式字符串**得到特定格式: +我们已经看到,格式化的方式是通过**格式字符串**来指定的: * `format!("{}", foo)` -> `"3735928559"` * `format!("0x{:X}", foo)` -> [`"0xDEADBEEF"`][deadbeef] * `format!("0o{:o}", foo)` -> `"0o33653337357"` -根据使用的**参数类型**,同样的变量(`foo`)能够格式化成不同的形式:`X`, `o` 和**未指定形式**。 +根据使用的**参数类型**是 `X`、`o` 还是**未指定**,同样的变量(`foo`)能够格式化 +成不同的形式。 -这个格式化的功能是通过 trait 实现,并且是一种 trait 来实现各种参数类型。最常见的格式化 trait -就是 `Display`,它可以处理多种情形,但没有指明参数类型,比如 `{}`。 +这个格式化的功能是通过 trait 实现的,每种参数类型都对应一种 trait。最常见的格式 +化 trait 就是 `Display`,它可以处理参数类型为未指定的情况,比如 `{}`。 ```rust,editable use std::fmt::{self, Formatter, Display}; @@ -24,13 +25,13 @@ struct City { } impl Display for City { - // `f` 是一个缓冲区(buffer),此方法必须将格式化的字符串写入其中 + // `f` 是一个缓冲区(buffer),此方法必须将格式化后字符串写入其中 fn fmt(&self, f: &mut Formatter) -> fmt::Result { let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' }; let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' }; - // `write!` 和 `format!` 类似,但它会将格式化后的字符串写入到一个缓冲区 - // 中(第一个参数f) + // `write!` 和 `format!` 类似,但它会将格式化后的字符串写入 + // 一个缓冲区(即第一个参数f)中。 write!(f, "{}: {:.3}°{} {:.3}°{}", self.name, self.lat.abs(), lat_c, self.lon.abs(), lon_c) } @@ -56,25 +57,27 @@ fn main() { Color { red: 0, green: 3, blue: 254 }, Color { red: 0, green: 0, blue: 0 }, ].iter() { - // 一旦添加了针对 fmt::Display 的实现,则要用 {} 对输出内容进行转换 + // 在添加了针对 fmt::Display 的实现后,请改用 {} 检验效果。 println!("{:?}", *color) } } ``` -在 [`fmt::fmt`][fmt] 文档中可以查看[全部系列的格式 traits][fmt_traits]和它们的参数类型。 +在 [`fmt::fmt`][fmt] 文档中可以查看[格式化 traits 一览表][fmt_traits]和它们的参 +数类型。 ### 动手试一试 -在上面的 `Color` 结构体加上一个 `fmt::Display` 的实现,得到如下的输出结果: +为上面的 `Color` 结构体实现 `fmt::Display`,应得到如下的输出结果: ```text RGB (128, 255, 90) 0x80FF5A RGB (0, 3, 254) 0x0003FE RGB (0, 0, 0) 0x000000 ``` + 如果感到疑惑,可看下面两条提示: - * 你[可能需要多次列出各种颜色][argument_types], - * 你可以使用 `:02` [补零使位数为2位]。 + * 你[可能需要多次列出每个颜色][argument_types], + * 你可以使用 `:02` [补零使位数为 2 位][fmt_width]。 ### 参见: [`std::fmt`][fmt] diff --git a/src/hello/print/print_debug.md b/src/hello/print/print_debug.md index 9d634e4c..e0e7dba5 100644 --- a/src/hello/print/print_debug.md +++ b/src/hello/print/print_debug.md @@ -1,49 +1,71 @@ # 调试 -所有要用到`std::fmt`格式化的`traits`类型都需要转化成可打印的实现。`std`库这些类型能够自动实现。但所有其他类型都必须手动来实现。 +所有的类型,若想用 `std::fmt` 的格式化 `trait` 打印出来,都要求实现这个 + `trait`。自动的实现只为一些类型提供,比如 `std` 库中的类型。所有其他类型 +都**必须**手动实现。 -`fmt::Debug` `trait` 使上面工作变得相当简单。所有类型都能推导(自动创建)`fmt::Debug` -的实现。但是 `fmt::Display` 需要手动来实现。 +`fmt::Debug` 这个 `trait` 使这项工作变得相当简单。所有类型都能推导(即自动创建) +`fmt::Debug` 的实现。但是 `fmt::Display` 需要手动实现。 ```rust -// 这种结构体不能使用`fmt::Display`或`fmt::Debug`来进行打印。 +// 这个结构体不能使用 `fmt::Display` 或 `fmt::Debug` 来进行打印。 struct UnPrintable(i32); -// `derive`属性会自动创建实现,借助`fmt::Debug`使得这个`struct`能够打印。 +// `derive` 属性会自动创建所需的实现,使这个 `struct` 能使用 `fmt::Debug` 打印。 #[derive(Debug)] struct DebugPrintable(i32); ``` -所有`std`库类型加上`{:?}`后也能够自动打印: +所有 `std` 库类型都天生可以使用 `{:?}` 来打印: ```rust,editable -// 从 `fmt::Debug` 获得实现给 `Structure`。 -// `Structure` 是一个包含`i32`基本类型的结构体。 +// 推导 `Structure` 的 `fmt::Debug` 实现。 +// `Structure` 是一个包含单个 `i32` 的结构体。 #[derive(Debug)] struct Structure(i32); -// 将 `Structure` 放到结构体 `Deep` 中。使 `Deep` 也能够打印。 +// 将 `Structure` 放到结构体 `Deep` 中。然后使 `Deep` 也能够打印。 #[derive(Debug)] struct Deep(Structure); fn main() { - // 打印操作使用 `{:?}` 和使用 `{}` 类似。 + // 使用 `{:?}` 打印和使用 `{}` 类似。 println!("{:?} months in a year.", 12); println!("{1:?} {0:?} is the {actor:?} name.", "Slater", "Christian", actor="actor's"); - // `Structure` 是能够打印的类型。 + // `Structure` 也可以打印! println!("Now {:?} will print!", Structure(3)); // 使用 `derive` 的一个问题是不能控制输出的形式。 - // 假如我只想展示一个 `7`? + // 假如我只想展示一个 `7` 怎么办? println!("Now {:?} will print!", Deep(Structure(7))); } ``` -所以 `fmt::Debug` 确实使这些内容可以打印,但是牺牲了美感。手动执行 `fmt::Display` 将能够弥补这些问题。 +所以 `fmt::Debug` 确实使这些内容可以打印,但是牺牲了一些美感。Rust 也通过 + `{:#?}` 提供了 “美化打印” 的功能: + +```rust,editable +#[derive(Debug)] +struct Person<'a> { + name: &'a str, + age: u8 +} + +fn main() { + let name = "Peter"; + let age = 27; + let peter = Person { name, age }; + + // 美化打印 + println!("{:#?}", peter); +} +``` + +你可以通过手动实现 `fmt::Display` 来控制显示效果。 ### 参见: diff --git a/src/hello/print/print_display.md b/src/hello/print/print_display.md index 34a6c647..2e84057c 100644 --- a/src/hello/print/print_display.md +++ b/src/hello/print/print_display.md @@ -1,66 +1,68 @@ # 显示 -`fmt::Debug` 看起来并不简洁,然而它对自定义输出外观通常是有好处的。而[`fmt::Display`] -[fmt]是通过手动的方式来实现,采用了`{}`来打印标记。实现方式看起来像这样: +`fmt::Debug` 通常看起来不太简洁,因此自定义输出的外观经常是更可取的。这需要通过 +手动实现 [`fmt::Display`][fmt] 来做到。`fmt::Display` 采用 `{}` 标记。实现方式看 +起来像这样: ```rust -// (使用 `use`)导入 `fmt` 模块使 `fmt::Display` 可用 +// (使用 `use`)导入 `fmt` 模块使 `fmt::Display` 可用 use std::fmt; -// 定义一个结构体,使用 `fmt::Display` 来实现。这只是简单地给元组结构体`Structure` 包含 -// 一个 `i32` 元素。 +// 定义一个结构体,咱们会为它实现 `fmt::Display`。以下是个简单的元组结构体 +// `Structure`,包含一个 `i32` 元素。 struct Structure(i32); -// 为了使用 `{}` 标记,必须手动实现 `fmt::Display` trait 来支持相应类型。 +// 为了使用 `{}` 标记,必须手动为类型实现 `fmt::Display` trait。 impl fmt::Display for Structure { - // 这个 trait 要求 `fmt` 带有正确的标记 + // 这个 trait 要求 `fmt` 使用与下面的函数完全一致的函数签名 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // 严格将第一个元素写入到给定的输出流 `f`。返回 `fmt:Result`,此结果表明操作成功 - // 或失败。注意这里的 `write!` 用法和 `println!` 很相似。 + // 仅将 self 的第一个元素写入到给定的输出流 `f`。返回 `fmt:Result`,此 + // 结果表明操作成功或失败。注意 `write!` 的用法和 `println!` 很相似。 write!(f, "{}", self.0) } } ``` -`fmt::display` 的使用形式可能比 `fmt::Debug` 简洁,但它对于标准库的处理有一个问题。模棱 -两可的类型该如何显示呢?举个例子,假设标准库对所有的 `Vec` 都实现了单一样式,那么它应该 -是那种样式?随意一种或者包含两种? +`fmt::display` 的效果可能比 `fmt::Debug` 简洁,但对于 `std` 库来说,这就有一个问 +题。模棱两可的类型该如何显示呢?举个例子,假设标准库对所有的 `Vec` 都实现了同 +一种输出样式,那么它应该是哪种样式?下面两种中的一种吗? -* `Vec`: `/:/etc:/home/username:/bin` (split on `:`) -* `Vec`: `1,2,3` (split on `,`) +* `Vec`:`/:/etc:/home/username:/bin`(使用 `:` 分割) +* `Vec`:`1,2,3`(使用 `,` 分割) -答案是否定的,因为没有合适的样式适用于所有类型,标准库也没规定一种情况。对于 `Vec` 或其 -他任意泛型容器(container),`fmt::Display` 都没有实现形式。在这种含有泛型的情况下要用到 - `fmt::Debug`。 +我们没有这样做,因为没有一种合适的样式适用于所有类型,标准库也并不擅自规定一种样 +式。对于 `Vec` 或其他任意泛型容器(generic container),`fmt::Display` 都没有 +实现。因此在这些泛型的情况下要用 `fmt::Debug`。 -而对于非泛型的容器类型的输出, `fmt::Display` 都能够实现。 +这并不是一个问题,因为对于任何**非**泛型的**容器**类型, `fmt::Display` 都能够实 +现。 ```rust,editable use std::fmt; // 导入 `fmt` -// 带有两个数字的结构体。`Debug` 将被派生,可以看到输出结果和 `Display` 的差异。 +// 带有两个数字的结构体。推导出 `Debug`,以便与 `Display` 的输出进行比较。 #[derive(Debug)] struct MinMax(i64, i64); // 实现 `MinMax` 的 `Display`。 impl fmt::Display for MinMax { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // 使用 `self.number` 方式来表示各个数据。 + // 使用 `self.number` 来表示各个数据。 write!(f, "({}, {})", self.0, self.1) } } -// 为了比较,定义一个含有字段的结构体。 +// 为了比较,定义一个含有具名字段的结构体。 #[derive(Debug)] struct Point2D { x: f64, y: f64, } -// 类似地对 Point2D 进行实现 +// 类似地对 `Point2D` 实现 `Display` impl fmt::Display for Point2D { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // 自定义方式实现,仅让 `x` 和 `y` 标识出来。 + // 自定义格式,使得仅显示 `x` 和 `y` 的值。 write!(f, "x: {}, y: {}", self.x, self.y) } } @@ -91,14 +93,15 @@ fn main() { } ``` -`fmt::Display` 都实现了,而 `fmt::Binary` 都没有,因此 `fmt::Binary` 不能使用。 -`std::fmt` 有很多这样的 [`traits`][traits],使用这些 trait 都要有各自的实现。这些内容将 -在后面的 [`std::fmt`][fmt] 章节中详细介绍。 +`fmt::Display` 被实现了,而 `fmt::Binary` 没有,因此 `fmt::Binary` 不能使用。 +`std::fmt` 有很多这样的 [`trait`][traits],它们都要求有各自的实现。这些内容将在 +后面的 [`std::fmt`][fmt] 章节中详细介绍。 ### 动手试一试 -对上面程序的运行结果检验完毕后,在上述示例程序中,仿照 `Point2` 结构体增加一个复数结构体。 -使用一样的方式打印,输出结果要求这个样子: +检验上面例子的输出,然后在示例程序中,仿照 `Point2D` 结构体增加一个复数结构体。 +使用一样的方式打印,输出结果要求是这个样子: + ``` Display: 3.3 + 7.2i Debug: Complex { real: 3.3, imag: 7.2 } diff --git a/src/hello/print/print_display/testcase_list.md b/src/hello/print/print_display/testcase_list.md index 44803421..fc8541c1 100644 --- a/src/hello/print/print_display/testcase_list.md +++ b/src/hello/print/print_display/testcase_list.md @@ -1,40 +1,48 @@ # 测试实例:List -对一个结构体来说,须对各个元素逐个实现 `fmt::Display` 可能会很麻烦。问题在于每个 `write!` -都要生成一个 `fmt::Result`。彻底地实现需要处理**所有**的结果。出于这方面考虑,Rust 提供了 `try!` 宏。 +对一个结构体实现 `fmt::Display`,其中的元素需要一个接一个地处理到,这可能会很麻 +烦。问题在于每个 `write!` 都要生成一个 `fmt::Result`。正确的实现需要 +处理**所有**的 Result。Rust 专门为解决这个问题提供了 `?` 操作符。 -在 `write!` 上使用 `try!`类似这样: +在 `write!` 上使用 `?` 会像是这样: ```rust,ignore // 对 `write!` 进行尝试(try),观察是否出错。若发生错误,返回相应的错误。 // 否则(没有出错)继续执行后面的语句。 +write!(f, "{}", value)?; +``` + +另外,你也可以使用 `try!` 宏,它和 `?` 是一样的。这种写法比较罗嗦,故不再推荐, +但在老一些的 Rust 代码中仍会看到。使用 `try!` 看起来像这样: + +```rust,ignore try!(write!(f, "{}", value)); ``` -在有 `try!` 的基础上,对一个 `Vec` 实现 `fmt::Display` 是相当直接的: +有了 `?`,对一个 `Vec` 实现 `fmt::Display` 就很简单了: ```rust,editable use std::fmt; // 导入 `fmt` 模块。 -// 定义一个包含不同包含 `Vec` 的结构体 `List`。 +// 定义一个包含单个 `Vec` 的结构体 `List`。 struct List(Vec); impl fmt::Display for List { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // 对 `self` 解引用,并通过解构创建一个指向 `vec` 的引用。 - let List(ref vec) = *self; + // 使用元组的脚标获取值,并创建一个 `vec` 的引用。 + let vec = &self.0; - try!(write!(f, "[")); + write!(f, "[")?; - // 对 `vec` 进行迭代,`v` 为每次迭代的值,`count` 为计数。 + // 使用 `v` 对 `vec` 进行迭代,并用 `count` 记录迭代次数。 for (count, v) in vec.iter().enumerate() { - // 在调用 `write!` 前对每个元素(第一个元素除外)加上逗号。 - // 使用 `try!` ,在出错的情况返回。 - if count != 0 { try!(write!(f, ", ")); } - try!(write!(f, "{}", v)); + // 对每个元素(第一个元素除外)加上逗号。 + // 使用 `?` 或 `try!` 来返回错误。 + if count != 0 { write!(f, ", ")?; } + write!(f, "{}", v)?; } - // 加上配对中括号,并返回一个 fmt::Result 值 + // 加上配对中括号,并返回一个 fmt::Result 值。 write!(f, "]") } } @@ -46,8 +54,9 @@ fn main() { ``` ### 动手试一试: -更改程序使 vector 里面元素的索引也能够打印出来。新的结果如下: -``` rust +更改程序使 vector 里面每个元素的脚标也能够打印出来。新的结果如下: + +```rust [0: 1, 1: 2, 2: 3] ``` From 4004db05d60fe4f7e89699e8b381d97fc887869c Mon Sep 17 00:00:00 2001 From: AmaiKinono Date: Sat, 10 Mar 2018 23:15:21 +0800 Subject: [PATCH 2/5] fix an error. --- src/hello/print/fmt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hello/print/fmt.md b/src/hello/print/fmt.md index c8b43a8c..3b85e481 100644 --- a/src/hello/print/fmt.md +++ b/src/hello/print/fmt.md @@ -25,7 +25,7 @@ struct City { } impl Display for City { - // `f` 是一个缓冲区(buffer),此方法必须将格式化后字符串写入其中 + // `f` 是一个缓冲区(buffer),此方法必须将格式化后的字符串写入其中 fn fmt(&self, f: &mut Formatter) -> fmt::Result { let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' }; let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' }; From ff6186311d273b08d3647d8609f3399adf3a0c32 Mon Sep 17 00:00:00 2001 From: AmaiKinono Date: Sun, 11 Mar 2018 10:06:28 +0800 Subject: [PATCH 3/5] =?UTF-8?q?change=20all=20"=E8=84=9A=E6=A0=87"=20to=20?= =?UTF-8?q?"=E4=B8=8B=E6=A0=87"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hello/print/print_display/testcase_list.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hello/print/print_display/testcase_list.md b/src/hello/print/print_display/testcase_list.md index fc8541c1..463f83ca 100644 --- a/src/hello/print/print_display/testcase_list.md +++ b/src/hello/print/print_display/testcase_list.md @@ -29,7 +29,7 @@ struct List(Vec); impl fmt::Display for List { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // 使用元组的脚标获取值,并创建一个 `vec` 的引用。 + // 使用元组的下标获取值,并创建一个 `vec` 的引用。 let vec = &self.0; write!(f, "[")?; @@ -54,7 +54,7 @@ fn main() { ``` ### 动手试一试: -更改程序使 vector 里面每个元素的脚标也能够打印出来。新的结果如下: +更改程序使 vector 里面每个元素的下标也能够打印出来。新的结果如下: ```rust [0: 1, 1: 2, 2: 3] From 949ca808bd93f702a845f9f6439e25eab3a89ee6 Mon Sep 17 00:00:00 2001 From: AmaiKinono Date: Sun, 11 Mar 2018 18:27:24 +0800 Subject: [PATCH 4/5] minor changes. --- src/SUMMARY.md | 4 ++-- src/hello/print.md | 2 +- src/hello/print/print_debug.md | 6 +++--- src/hello/print/print_display.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index c7c9e92f..bb443b9a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -5,8 +5,8 @@ - [Hello World](hello.md) - [注释](hello/comment.md) - [格式化输出](hello/print.md) - - [调试](hello/print/print_debug.md) - - [显示](hello/print/print_display.md) + - [调试(debug)](hello/print/print_debug.md) + - [显示(display)](hello/print/print_display.md) - [测试实例:List](hello/print/print_display/testcase_list.md) - [格式化](hello/print/fmt.md) diff --git a/src/hello/print.md b/src/hello/print.md index 6716463b..47157006 100644 --- a/src/hello/print.md +++ b/src/hello/print.md @@ -9,7 +9,7 @@ * `eprint!`:与 `format!` 类似,但将文本输出到标准错误(io::stderr)。 * `eprintln!`:与 `eprint!` 类似,但输出结果追加一个换行符。 -这些宏都以相同的做法解析文本。另外有个优点是格式化的正确性会在编译时检查。 +这些宏都以相同的做法解析(parse)文本。另外有个优点是格式化的正确性会在编译时检查。 ```rust,editable,ignore,mdbook-runnable fn main() { diff --git a/src/hello/print/print_debug.md b/src/hello/print/print_debug.md index e0e7dba5..cc5c7c5d 100644 --- a/src/hello/print/print_debug.md +++ b/src/hello/print/print_debug.md @@ -1,11 +1,11 @@ -# 调试 +# 调试(debug) 所有的类型,若想用 `std::fmt` 的格式化 `trait` 打印出来,都要求实现这个 `trait`。自动的实现只为一些类型提供,比如 `std` 库中的类型。所有其他类型 都**必须**手动实现。 -`fmt::Debug` 这个 `trait` 使这项工作变得相当简单。所有类型都能推导(即自动创建) -`fmt::Debug` 的实现。但是 `fmt::Display` 需要手动实现。 +`fmt::Debug` 这个 `trait` 使这项工作变得相当简单。所有类型都能推导(derive,即自 +动创建)`fmt::Debug` 的实现。但是 `fmt::Display` 需要手动实现。 ```rust // 这个结构体不能使用 `fmt::Display` 或 `fmt::Debug` 来进行打印。 diff --git a/src/hello/print/print_display.md b/src/hello/print/print_display.md index 2e84057c..85aed7df 100644 --- a/src/hello/print/print_display.md +++ b/src/hello/print/print_display.md @@ -1,4 +1,4 @@ -# 显示 +# 显示(display) `fmt::Debug` 通常看起来不太简洁,因此自定义输出的外观经常是更可取的。这需要通过 手动实现 [`fmt::Display`][fmt] 来做到。`fmt::Display` 采用 `{}` 标记。实现方式看 From 90c04b9a45860c80705e62eef9065e3cd2ec1949 Mon Sep 17 00:00:00 2001 From: Aaklo Xu Date: Thu, 18 Apr 2019 19:46:21 +0800 Subject: [PATCH 5/5] Update hello.md --- src/hello.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hello.md b/src/hello.md index 84ab6f05..971688dd 100644 --- a/src/hello.md +++ b/src/hello.md @@ -19,7 +19,7 @@ fn main() { } ``` -`println!` 是一个[**宏**][macros](macro),可以将文本输出到控制台(console)。 +`println!` 是一个[**宏**][macros](macros),可以将文本输出到控制台(console)。 使用 Rust 的编译器 `rustc` 可以从源程序生成可执行文件: @@ -36,8 +36,7 @@ Hello World! ### 动手试一试 -单击上面的 "Run" 按钮并观察输出结果。然后增加一行代码,再一次使用宏 `println!`, -得到下面结果: +单击上面的 "Run" 按钮并观察输出结果。然后增加一行代码,再一次使用宏 `println!`,得到下面结果: ```text Hello World!