显示

fmt::Debug 看起来并不紧凑和简洁,因此自定义输出外观通常是有利的。这是通过手动实现 fmt::Display 来完成的,它使用 {} 打印标记。实现它的方式如下

#![allow(unused)]
fn main() {
// Import (via `use`) the `fmt` module to make it available.
use std::fmt;

// Define a structure for which `fmt::Display` will be implemented. This is
// a tuple struct named `Structure` that contains an `i32`.
struct Structure(i32);

// To use the `{}` marker, the trait `fmt::Display` must be implemented
// manually for the type.
impl fmt::Display for Structure {
    // This trait requires `fmt` with this exact signature.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Write strictly the first element into the supplied output
        // stream: `f`. Returns `fmt::Result` which indicates whether the
        // operation succeeded or failed. Note that `write!` uses syntax which
        // is very similar to `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; // Import `fmt`

// A structure holding two numbers. `Debug` will be derived so the results can
// be contrasted with `Display`.
#[derive(Debug)]
struct MinMax(i64, i64);

// Implement `Display` for `MinMax`.
impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Use `self.number` to refer to each positional data point.
        write!(f, "({}, {})", self.0, self.1)
    }
}

// Define a structure where the fields are nameable for comparison.
#[derive(Debug)]
struct Point2D {
    x: f64,
    y: f64,
}

// Similarly, implement `Display` for `Point2D`.
impl fmt::Display for Point2D {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Customize so only `x` and `y` are denoted.
        write!(f, "x: {}, y: {}", self.x, self.y)
    }
}

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

    println!("Compare structures:");
    println!("Display: {}", minmax);
    println!("Debug: {:?}", minmax);

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

    println!("The big range is {big} and the small is {small}",
             small = small_range,
             big = big_range);

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

    println!("Compare points:");
    println!("Display: {}", point);
    println!("Debug: {:?}", point);

    // Error. Both `Debug` and `Display` were implemented, but `{:b}`
    // requires `fmt::Binary` to be implemented. This will not work.
    // println!("What does Point2D look like in binary: {:b}?", point);
}

因此,已经实现了 fmt::Display,但没有实现 fmt::Binary,因此无法使用。std::fmt 有许多这样的 特征,每个特征都需要自己的实现。这在 std::fmt 中有更详细的说明。

活动

在检查了上面示例的输出后,以 Point2D 结构体为指导,向示例中添加一个 Complex 结构体。以相同的方式打印时,输出应为

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

另请参阅

derivestd::fmt结构体特征use