显示 Display - Rust By Example
Description: Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust concepts and standard libraries.

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# 显示 Display

`fmt::Debug` 的输出往往不够简洁清晰，因此自定义输出外观通常更有优势。 这可以通过手动实现 `fmt::Display` 来完成， 它使用 `{}` 打印标记。实现方式如下：

    #![allow(unused)]
    fn main() {
    // 通过 `use` 导入 `fmt` 模块使其可用。
    use std::fmt;

    // 定义一个结构体，我们将为其实现 `fmt::Display`。
    // 这是一个名为 `Structure` 的元组结构体，包含一个 `i32`。
    struct Structure(i32);

    // 要使用 `{}` 标记，必须为该类型手动实现 `fmt::Display` trait。
    impl fmt::Display for Structure {
        // 这个 trait 要求 `fmt` 方法具有确切的签名。
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            // 将第一个元素严格写入提供的输出流 `f`。
            // 返回 `fmt::Result`，表示操作是否成功。
            // 注意 `write!` 的语法与 `println!` 非常相似。
            write!(f, "{}", self.0)
        }
    }
    }

`fmt::Display` 可能比 `fmt::Debug` 更简洁，但这给 `std` 库带来了一个问题：如何显示歧义类型？例如，如果 `std` 库为所有 `Vec<T>` 实现统一的样式，应该采用哪种样式？是以下两种之一吗？

- `Vec<path>`：`/:/etc:/home/username:/bin`（以 `:` 分隔）
- `Vec<number>`：`1,2,3`（以 `,` 分隔）

答案是否定的。因为不存在适用于所有类型的理想样式，`std` 库也不应擅自规定一种。因此，`fmt::Display` 并未为 `Vec<T>` 或其他泛型容器实现。在这些泛型情况下，必须使用 `fmt::Debug`。

不过，这不是问题。对于任何新的非泛型容器类型，都可以实现 `fmt::Display`。

    use std::fmt; // 导入 `fmt`

    // 定义一个包含两个数字的结构体。派生 `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` 引用每个位置的数据点。
            write!(f, "({}, {})", self.0, self.1)
        }
    }

    // 定义一个结构体，其字段可命名以便比较。
    #[derive(Debug)]
    struct Point2D {
        x: f64,
        y: f64,
    }

    // 同样，为 `Point2D` 实现 `Display` 特性。
    impl fmt::Display for Point2D {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            // 自定义实现，只显示 `x` 和 `y`。
            write!(f, "x: {}, y: {}", self.x, self.y)
        }
    }

    fn main() {
        let minmax = MinMax(0, 14);

        println!("比较结构体：");
        println!("Display：{}", minmax);
        println!("Debug：{:?}", minmax);

        let big_range =   MinMax(-300, 300);
        let small_range = MinMax(-3, 3);

        println!("大范围是 {big}，小范围是 {small}",
                 small = small_range,
                 big = big_range);

        let point = Point2D { x: 3.3, y: 7.2 };

        println!("比较点：");
        println!("Display：{}", point);
        println!("Debug：{:?}", point);

        // 错误。虽然实现了 `Debug` 和 `Display`，但 `{:b}` 需要
        // 实现 `fmt::Binary`。这行代码无法工作。
        // println!("Point2D 的二进制表示是什么：{:b}？", point);
    }

因此，虽然实现了 `fmt::Display`，但未实现 `fmt::Binary`，所以无法使用。`std::fmt` 包含许多这样的`traits`，每个都需要单独实现。更多详情请参阅 `std::fmt`。

### 练习

查看上述示例的输出后，参考 `Point2D` 结构体，向示例中添加一个 `Complex` 结构体。以相同方式打印时，输出应为：

    Display: 3.3 +7.2i
    Debug: Complex { real: 3.3, imag: 7.2 }

    Display: 4.7 -2.3i
    Debug: Complex { real: 4.7, imag: -2.3 }

额外练习：在 `+`/`-` 符号前添加一个空格。

如果你遇到困难，这里有三个提示：

- 你可以使用 `:0>2` 用零填充到宽度为 2。
- 额外练习：查看 `if`-`else` 分支语句以及 `abs` 函数。

### 另请参阅：

`derive`、`std::fmt`、`macros`、`struct`、`trait` 和 `use`