1.2.2 Display

fmt::Debug는 간결하고 깨끗하게 보긴 어렵기에 출력 형태를 사용자 정의 하는게 많은 장점을 갖는다. 이는 수작업으로 {}를 출력 기호로 사용하는 fmt::Display를 구현하여 작업한다. 구현은 아래와 같다: fmt::Debug hardly looks compact and clean, so it is often advantageous to customize the output appearance. This is done by manually implementing fmt::Display, which uses the {} print marker. Implementing it looks like this:

// (`use`를 통해) `fmt` module을 import해 사용할 수 있게 한다.
// Import (via `use`) the `fmt` module to make it available.
use std::fmt;

// `fmt::Display` 구조체를 정의하여 이를 구현할 수 있게 한다. 
// 여기서는 간단하게 `i32`를 포함하는 튜플 구조로 `Structure`란 이름으로 바인드한다.
// Define a structure which `fmt::Display` will be implemented for. This is simply
// a tuple struct containing an `i32` bound to the name `Structure`.
struct Structure(i32);

// `{}` 기호를 사용하기 위해서, `fmt::Display`가 타입에 대해 반드시 구현되어야 한다.
// In order 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::Displayfmt::Debug보다 깨끗할지는 모르겠지만 이는 std라이브러리에 적용하긴 문제가 있다. 어떻게 모호한 타입도 출력되도록 할 수 있을까? 예를 들어, std라이브러리가 모든 Vec<T>를 하나의 스타일을 구현한다고 하면, 다음 둘에 대해 어떤 스타일이 적용되야 할까? fmt::Display may be cleaner than fmt::Debug but this presents a problem for the std library. How should ambiguous types be displayed? For example, if the std library implemented a single style for all Vec<T>, what style should it be? Either of these two?

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

놉, 모든 카입을 위한 이상적인 스타일은 존재하지 않고 std 라이브러리는 하나로 규정될거라 가정하지 않는다. fmt::DisplayVec<T>나 모든 다른 제네릭 컨테이너에는 구현되지 않는다. fmt::Debug가 이런 제네릭 경우에 사용될 수 있다. No, because there is no ideal style for all types and the std library doesn't presume to dictate one. fmt::Display is not implemented for Vec<T> or for any other generic containers. fmt::Debug must then be used for these generic cases.

이런 문제는 제네릭 컨테이너에는 문제가 아니고 fmt::Dispaly를 구현할 수 있다. This is not a problem though because for any new container type which is not generic,fmt::Display can be implemented.

use std::fmt; // Import `fmt`

// 구조체는 두 숫자를 보관한다. `Debug`가 파생되어 `Display`의 결과와 대조된다.
// A structure holding two numbers. `Debug` will be derived so the results can
// be contrasted with `Display`.
#[derive(Debug)]
struct MinMax(i64, i64);

// `Display`를 `MinMax`에 구현한다.
// 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 Point2 {
    x: f64,
    y: f64,
}

// 비슷하게 Point2를 위해 구현한다.
// Similarly, implement for Point2
impl fmt::Display for Point2 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // 사용자 정의를 통해 `x`와 `y`만 표시되도록 한다.
        // 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 = Point2 { x: 3.3, y: 7.2 };

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

    // 에러. `Debug`와 `Display` 둘이 구현되었지만 `{:b}`는 
    // `fmt::Binary`의 구현이 요구된다. 이는 동작하지 않는다.
    // 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는 이런 traits을 많이 보유하고 있으며 이들은 각자 그 자신의 구현을 요구한다. 여기 std::fmt에 더 상세한 내용이 있다. So, fmt::Display has been implemented but fmt::Binary has not, and therefore cannot be used. std::fmt has many such traits and each requires its own implementation. This is detailed further in std::fmt.

Activity

상기 예제의 출력을 확인한 후에, Point2 구조체를 견본으로 삼아 Complex 구조체를 예제에 추가해보자. 동일한 방식으로 출력하면, 출력은 아래와 같이 나오게 될 것이다: After checking the output of the above example, use the Point2 struct as guide to add a Complex struct to the example. When printed in the same way, the output should be:

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

See also

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

results matching ""

    No results matching ""