Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
16 changes: 8 additions & 8 deletions src/hello.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@

```rust,editable
// 这是注释内容,将会被编译器忽略掉
// 可以单击前面的按钮 "Run" 来测试这段代码 ->
// 若想用键盘操作,可以使用快捷键"Ctrl + Enter"来运行
// 可以单击那边的按钮 "Run" 来测试这段代码 ->
// 若想用键盘操作,可以使用快捷键 "Ctrl + Enter" 来运行

// 这段代码支持编辑,你可以自由地改进代码
// 这段代码支持编辑,你可以自由地修改代码
// 通过单击 "Reset" 按钮可以使代码恢复到初始状态 ->

// 这是主函数
fn main() {
// 调用已编译成的可执行文件时,在这里面的语句将会运行
// 调用编译生成的可执行文件时,这里的语句将被运行。

// 将文本打印到控制台
println!("Hello World!");
}
```

`println!` 是一个 [**宏**][macros](macros),可以将文本输出到控制台(console)。
`println!` 是一个[**宏**][macros](macros),可以将文本输出到控制台(console)。

使用 Rust 的编译器 `rustc` 可以将源程序生成可执行文件
使用 Rust 的编译器 `rustc` 可以从源程序生成可执行文件

```bash
$ rustc hello.rs
```

`rustc` 编译后将得到可执行文件 `hello`。
使用 `rustc` 编译后将得到可执行文件 `hello`。

```bash
$ ./hello
Expand All @@ -36,7 +36,7 @@ Hello World!

### 动手试一试

单击上面的 'Run' 按钮并观察输出结果。然后增加一行代码,再一次使用宏 `println!` 得到下面结果:
单击上面的 "Run" 按钮并观察输出结果。然后增加一行代码,再一次使用宏 `println!`得到下面结果:

```text
Hello World!
Expand Down
23 changes: 11 additions & 12 deletions src/hello/comment.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
56 changes: 31 additions & 25 deletions src/hello/print.md
Original file line number Diff line number Diff line change
@@ -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!` 类似,但输出结果追加一个换行符。

所有的解析文本都以相同的方式进行。另外一点是格式化的正确性在编译时检查
这些宏都以相同的做法解析(parse)文本。另外有个优点是格式化的正确性会在编译时检查

```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");

// 可以使用赋值语句。
Expand All @@ -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
Expand Down
27 changes: 15 additions & 12 deletions src/hello/print/fmt.md
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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)
}
Expand All @@ -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]
Expand Down
50 changes: 36 additions & 14 deletions src/hello/print/print_debug.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,71 @@
# 调试
# 调试(debug)

所有要用到`std::fmt`格式化的`traits`类型都需要转化成可打印的实现。`std`库这些类型能够自动实现。但所有其他类型都必须手动来实现。
所有的类型,若想用 `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`来进行打印。
// 这个结构体不能使用 `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` 来控制显示效果。

### 参见:

Expand Down
Loading