Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

print.md synchronizes official documents #190

Merged
merged 4 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
91 changes: 56 additions & 35 deletions english/src/hello/print.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Formatted print

Printing is handled by a series of [`macros`][macros] defined in [`std::fmt`][fmt]
some of which include:
Printing is handled by a series of [`macros`][macros] defined in
[`std::fmt`][fmt] some of which include:

* `format!`: write formatted text to [`String`][string]
* `print!`: same as `format!` but the text is printed to the console (io::stdout).
* `print!`: same as `format!` but the text is printed to the console
(io::stdout).
* `println!`: same as `print!` but a newline is appended.
* `eprint!`: same as `format!` but the text is printed to the standard error (io::stderr).
* `eprintln!`: same as `eprint!`but a newline is appended.
* `eprint!`: same as `print!` but the text is printed to the standard error
(io::stderr).
* `eprintln!`: same as `eprint!` but a newline is appended.

All parse text in the same fashion. As a plus, Rust checks formatting
correctness at compile time.
Expand All @@ -18,11 +20,9 @@ fn main() {
// arguments. These will be stringified.
println!("{} days", 31);

// Without a suffix, 31 becomes an i32. You can change what type 31 is
// by providing a suffix. The number 31i64 for example has the type i64.

// There are various optional patterns this works with. Positional
// arguments can be used.
// Positional arguments can be used. Specifying an integer inside `{}`
// determines which additional argument will be replaced. Arguments start
// at 0 immediately after the format string.
println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");

// As can named arguments.
Expand All @@ -31,29 +31,46 @@ fn main() {
subject="the quick brown fox",
verb="jumps over");

// Special formatting can be specified after a `:`.
println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
// Different formatting can be invoked by specifying the format character
// after a `:`.
println!("Base 10: {}", 69420); // 69420
println!("Base 2 (binary): {:b}", 69420); // 10000111100101100
println!("Base 8 (octal): {:o}", 69420); // 207454
println!("Base 16 (hexadecimal): {:x}", 69420); // 10f2c

// You can right-justify text with a specified width. This will
// output " 1". (Four white spaces and a "1", for a total width of 5.)
println!("{number:>5}", number=1);

// You can right-align text with a specified width. This will output
// " 1". 5 white spaces and a "1".
println!("{number:>width$}", number=1, width=6);
// You can pad numbers with extra zeroes,
println!("{number:0>5}", number=1); // 00001
// and left-adjust by flipping the sign. This will output "10000".
println!("{number:0<5}", number=1); // 10000

// You can pad numbers with extra zeroes. This will output "000001".
println!("{number:0>width$}", number=1, width=6);
// You can use named arguments in the format specifier by appending a `$`.
println!("{number:0>width$}", number=1, width=5);

// Rust even checks to make sure the correct number of arguments are
// used.
// Rust even checks to make sure the correct number of arguments are used.
println!("My name is {0}, {1} {0}", "Bond");
// FIXME ^ Add the missing argument: "James"

// Create a structure named `Structure` which contains an `i32`.
#[allow(dead_code)]
// Only types that implement fmt::Display can be formatted with `{}`. User-
// defined types do not implement fmt::Display by default.

#[allow(dead_code)] // disable `dead_code` which warn against unused module
struct Structure(i32);

// However, custom types such as this structure require more complicated
// handling. This will not work.
println!("This struct `{}` won't print...", Structure(3));
// FIXME ^ Comment out this line.
// This will not compile because `Structure` does not implement
// fmt::Display.
// println!("This struct `{}` won't print...", Structure(3));
// TODO ^ Try uncommenting this line

// For Rust 1.58 and above, you can directly capture the argument from a
// surrounding variable. Just like the above, this will output
// " 1", 4 white spaces and a "1".
let number: f64 = 1.0;
let width: usize = 5;
println!("{number:>width$}");
}
```

Expand All @@ -62,28 +79,30 @@ of text. The base form of two important ones are listed below:

* `fmt::Debug`: Uses the `{:?}` marker. Format text for debugging purposes.
* `fmt::Display`: Uses the `{}` marker. Format text in a more elegant, user
friendly fashion.
friendly fashion.

Here, we used `fmt::Display` because the std library provides implementations
for these types. To print text for custom types, more steps are required.

Implementing the `fmt::Display` trait automatically implements the
[`ToString`] trait which allows us to [convert] the type to [`String`][string].

In *line 43*, `#[allow(dead_code)]` is an [attribute] which only applies to the module after it.

### Activities

* Fix the two issues in the above code (see FIXME) so that it runs without
error.
* Add a `println!` macro that prints: `Pi is roughly 3.142` by controlling
the number of decimal places shown. For the purposes of this exercise,
use `let pi = 3.141592` as an estimate for pi. (Hint: you may need to
check the [`std::fmt`][fmt] documentation for setting the number of
decimals to display)
* Fix the issue in the above code (see FIXME) so that it runs without
error.
* Try uncommenting the line that attempts to format the `Structure` struct
(see TODO)
* Add a `println!` macro call that prints: `Pi is roughly 3.142` by controlling
the number of decimal places shown. For the purposes of this exercise, use
`let pi = 3.141592` as an estimate for pi. (Hint: you may need to check the
[`std::fmt`][fmt] documentation for setting the number of decimals to display)

### See also:

[`std::fmt`][fmt], [`macros`][macros], [`struct`][structs],
and [`traits`][traits]
[`std::fmt`][fmt], [`macros`][macros], [`struct`][structs], [`traits`][traits], and [`dead_code`][dead_code]

[fmt]: https://doc.rust-lang.org/std/fmt/
[macros]: ../macros.md
Expand All @@ -92,3 +111,5 @@ and [`traits`][traits]
[traits]: https://doc.rust-lang.org/std/fmt/#formatting-traits
[`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html
[convert]: ../conversion/string.md
[attribute]: ../attribute.md
[dead_code]: ../attribute/unused.md
64 changes: 43 additions & 21 deletions src/hello/print.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ fn main() {
// 变量内容会转化成字符串。
println!("{} days", 31);

// 不加后缀的话,31 就自动成为 i32 类型。
// 你可以添加后缀来改变 31 的类型(例如使用 31i64 声明 31 为 i64 类型)。

// 用变量替换字符串有多种写法。
// 比如可以使用位置参数。
// 可以使用位置参数。
// 在 `{}` 中指定一个整数确定将替换哪个附加参数。
// 参数从紧接在格式字符串之后的 0 开始。
println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");

// 可以使用命名参数。
Expand All @@ -29,49 +27,73 @@ fn main() {
subject="the quick brown fox",
verb="jumps over");

// 可以在 `:` 后面指定特殊的格式。
println!("{} of {:b} people know binary, the other half don't", 1, 2);
// 通过在 `:` 后面指定格式字符,可以调用不同的格式。
println!("Base 10: {}", 69420); // 69420
println!("Base 2 (binary): {:b}", 69420); // 10000111100101100
println!("Base 8 (octal): {:o}", 69420); // 207454
println!("Base 16 (hexadecimal): {:x}", 69420); // 10f2c

// 可以用指定的宽度对文本进行右对齐。
// 这将输出“ 1”。(四个空格和一个“1”,总宽度为5)
println!("{number:>5}", number=1);

// 你可以按指定宽度来右对齐文本。
// 下面语句输出 " 1",5 个空格后面连着 1。
println!("{number:>width$}", number=1, width=6);
// 你可以在数字左边补 0,
println!("{number:0>5}", number=1); // 00001
// 在数字右边补零,下面语句输出 "000001"。
println!("{number:0<5}", number=1); // 10000

// 你可以在数字左边补 0。下面语句输出 "000001"
println!("{number:>0width$}", number=1, width=6);
// 可以通过添加 `$` 在格式说明符中使用命名参数
println!("{number:0>width$}", number=1, width=5);

// println! 会检查使用到的参数数量是否正确。
println!("My name is {0}, {1} {0}", "Bond");
// 改正 ^ 补上漏掉的参数:"James"
// FIXME ^ 补上漏掉的参数:"James"

// 创建一个包含单个 `i32` 的结构体(structure)。命名为 `Structure`。
#[allow(dead_code)]
// 只有实现了fmt::Display的类型才能使用'{}'进行格式化。
// 默认情况下,用户定义的类型不实现fmt::Display。
#[allow(dead_code)] // 禁用对未使用模块发出警告的' dead_code '
struct Structure(i32);

// 但是像结构体这样的自定义类型需要更复杂的方式来处理。
// 这将不会编译,因为 `Structure` 没有实现
// 下面语句无法运行。
println!("This struct `{}` won't print...", Structure(3));
// 改正 ^ 注释掉此行。
// println!("This struct `{}` won't print...", Structure(3));
// TODO ^ 注释掉此行。

// 对于 Rust 1.58 及更高版本,您可以直接从周围变量捕获参数。
YangFong marked this conversation as resolved.
Show resolved Hide resolved
// 就像上面一样,这将输出“ 1”,4 个空格和 1 个“1”。
let number: f64 = 1.0;
let width: usize = 5;
println!("{number:>width$}");
}
```

[`std::fmt`][fmt] 包含多种 [`trait`][traits](特质)来控制文字显示,其中重要的两种 trait 的基本形式如下:
[`std::fmt`][fmt] 包含多种 [`traits`][traits](特质)来控制文字显示,其中重要的两种 trait 的基本形式如下:

- `fmt::Debug`:使用 `{:?}` 标记。格式化文本以供调试使用。
- `fmt::Display`:使用 `{}` 标记。以更优雅和友好的风格来格式化文本。

上例使用了 `fmt::Display`,因为标准库提供了那些类型的实现。若要打印自定义类型的文本,需要更多的步骤。

实现 `fmt::Display` 特性会自动实现 [`ToString`] 特征,它允许我们将类型 [convert] 为 [`String`][string]。

在*第 43 行*中,`#[allow(dead_code)]` 是一个 [attribute],只适用于它后面的模块。

### 动手试一试

- 改正上面代码中的两个错误(见 “改正”),使它可以没有错误地运行。
- 改正上面代码中的错误(见 FIXME),使它可以没有错误地运行。
- 尝试取消注释,输出结构体内容(见 TODO)。
- 再用一个 `println!` 宏,通过控制显示的小数位数来打印:`Pi is roughly 3.142`(Pi 约等于 3.142)。为了达到练习目的,使用 `let pi = 3.141592` 作为 Pi 的近似值(提示:设置小数位的显示格式可以参考文档 [`std::fmt`][fmt])。

### 参见:

[`std::fmt`][fmt], [`macros`][macros], [`struct`][structs] 和 [`trait`][traits]
[`std::fmt`][fmt], [`macros`][macros], [`struct`][structs], [`traits`][traits] 和 [`dead_code`][dead_code]

[fmt]: https://rustwiki.org/zh-CN/std/fmt/
[macros]: ../macros.md
[string]: ../std/str.md
[structs]: ../custom_types/structs.md
[traits]: ../trait.md
[`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html
[convert]: ../conversion/string.md
[attribute]: ../attribute.md
[dead_code]: ../attribute/unused.md