• 显示
    • 动手试一试
    • 参见:

    显示

    fmt::Debug 看起来并不简洁,然而它对自定义输出外观通常是有好处的。而fmt::Display是通过手动的方式来实现,采用了{}来打印标记。实现方式看起来像这样:

    1. // (使用 `use`)导入 `fmt` 模块使 `fmt::Display` 可用
    2. use std::fmt;
    3. // 定义一个结构体,使用 `fmt::Display` 来实现。这只是简单地给元组结构体`Structure` 包含
    4. // 一个 `i32` 元素。
    5. struct Structure(i32);
    6. // 为了使用 `{}` 标记,必须手动实现 `fmt::Display` trait 来支持相应类型。
    7. impl fmt::Display for Structure {
    8. // 这个 trait 要求 `fmt` 带有正确的标记
    9. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    10. // 严格将第一个元素写入到给定的输出流 `f`。返回 `fmt:Result`,此结果表明操作成功
    11. // 或失败。注意这里的 `write!` 用法和 `println!` 很相似。
    12. write!(f, "{}", self.0)
    13. }
    14. }

    fmt::display 的使用形式可能比 fmt::Debug 简洁,但它对于标准库的处理有一个问题。模棱
    两可的类型该如何显示呢?举个例子,假设标准库对所有的 Vec<T> 都实现了单一样式,那么它应该
    是那种样式?随意一种或者包含两种?

    • Vec<path>: /:/etc:/home/username:/bin (split on :)
    • Vec<number>: 1,2,3 (split on ,)

    答案是否定的,因为没有合适的样式适用于所有类型,标准库也没规定一种情况。对于 Vec<T> 或其
    他任意泛型容器(container),fmt::Display 都没有实现形式。在这种含有泛型的情况下要用到
    fmt::Debug

    而对于非泛型的容器类型的输出, fmt::Display 都能够实现。

    1. use std::fmt; // 导入 `fmt`
    2. // 带有两个数字的结构体。`Debug` 将被派生,可以看到输出结果和 `Display` 的差异。
    3. #[derive(Debug)]
    4. struct MinMax(i64, i64);
    5. // 实现 `MinMax` 的 `Display`。
    6. impl fmt::Display for MinMax {
    7. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    8. // 使用 `self.number` 方式来表示各个数据。
    9. write!(f, "({}, {})", self.0, self.1)
    10. }
    11. }
    12. // 为了比较,定义一个含有字段的结构体。
    13. #[derive(Debug)]
    14. struct Point2D {
    15. x: f64,
    16. y: f64,
    17. }
    18. // 类似地对 Point2D 进行实现
    19. impl fmt::Display for Point2D {
    20. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    21. // 自定义方式实现,仅让 `x` 和 `y` 标识出来。
    22. write!(f, "x: {}, y: {}", self.x, self.y)
    23. }
    24. }
    25. fn main() {
    26. let minmax = MinMax(0, 14);
    27. println!("Compare structures:");
    28. println!("Display: {}", minmax);
    29. println!("Debug: {:?}", minmax);
    30. let big_range = MinMax(-300, 300);
    31. let small_range = MinMax(-3, 3);
    32. println!("The big range is {big} and the small is {small}",
    33. small = small_range,
    34. big = big_range);
    35. let point = Point2D { x: 3.3, y: 7.2 };
    36. println!("Compare points:");
    37. println!("Display: {}", point);
    38. println!("Debug: {:?}", point);
    39. // 报错。`Debug` 和 `Display` 都被实现了,但 `{:b}` 需要 `fmt::Binary`
    40. // 得到实现。这语句不能运行。
    41. // println!("What does Point2D look like in binary: {:b}?", point);
    42. }

    fmt::Display 都实现了,而 fmt::Binary 都没有,因此 fmt::Binary 不能使用。
    std::fmt 有很多这样的 traits,使用这些 trait 都要有各自的实现。这些内容将
    在后面的 std::fmt 章节中详细介绍。

    动手试一试

    对上面程序的运行结果检验完毕后,在上述示例程序中,仿照 Point2 结构体增加一个复数结构体。
    使用一样的方式打印,输出结果要求这个样子:

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

    参见:

    derive, std::fmt, macros, struct,
    trait, 和 use