Настройка вывода структуры в Rust с использованием fmt::Display

RustRustBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом практическом занятии вы научитесь реализовывать трейт fmt::Display в Rust для настройки внешнего вида вывода структуры. Также вы изучите разницу между fmt::Display и fmt::Debug, а также ограничения fmt::Display для обобщенных типов контейнеров. Наконец, у вас будет задание по реализации трейта fmt::Display для новой структуры Complex и выводу ее в определенном формате.

Примечание: Если практическое занятие не задает имя файла, вы можете использовать любое имя, которое хотите. Например, вы можете использовать main.rs, скомпилировать и запустить его с помощью rustc main.rs &&./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/AdvancedTopicsGroup(["Advanced Topics"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/DataTypesGroup -.-> rust/integer_types("Integer Types") rust/DataTypesGroup -.-> rust/floating_types("Floating-point Types") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/AdvancedTopicsGroup -.-> rust/traits("Traits") subgraph Lab Skills rust/variable_declarations -.-> lab-99188{{"Настройка вывода структуры в Rust с использованием fmt::Display"}} rust/integer_types -.-> lab-99188{{"Настройка вывода структуры в Rust с использованием fmt::Display"}} rust/floating_types -.-> lab-99188{{"Настройка вывода структуры в Rust с использованием fmt::Display"}} rust/function_syntax -.-> lab-99188{{"Настройка вывода структуры в Rust с использованием fmt::Display"}} rust/expressions_statements -.-> lab-99188{{"Настройка вывода структуры в Rust с использованием fmt::Display"}} rust/traits -.-> lab-99188{{"Настройка вывода структуры в Rust с использованием fmt::Display"}} end

Display

fmt::Debug редко выглядит компактным и чистым, поэтому часто выгодно настроить внешний вид вывода. Это делается путём ручного реализации fmt::Display, который использует маркер вывода {}. Реализация выглядит так:

// Импортируем (через `use`) модуль `fmt`, чтобы иметь возможность использовать его.
use std::fmt;

// Определяем структуру, для которой будет реализован `fmt::Display`. Это
// кортежная структура под названием `Structure`, которая содержит `i32`.
struct Structure(i32);

// Чтобы использовать маркер `{}`, для типа необходимо вручную реализовать
// трейт `fmt::Display`.
impl fmt::Display for Structure {
    // Этот трейт требует `fmt` с такой точной сигнатурой.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Точно записываем первый элемент в предоставляемый поток вывода: `f`.
        // Возвращает `fmt::Result`, который показывает, удалось ли выполнить
        // операцию или нет. Обратите внимание, что `write!` использует синтаксис,
        // очень похожий на `println!`.
        write!(f, "{}", self.0)
    }
}

fmt::Display может быть чище, чем fmt::Debug, но это представляет проблему для стандартной библиотеки std. Как следует выводить неоднозначные типы? Например, если стандартная библиотека реализовала единый стиль для всех Vec<T>, какой стиль должен быть выбран? Будет ли это один из двух вариантов?

  • Vec<путь>: /:/etc:/home/username:/bin (разделено по :)
  • Vec<число>: 1,2,3 (разделено по ,)

Нет, потому что для всех типов нет идеального стиля, и стандартная библиотека не настаивает на одном стиле. fmt::Display не реализован для Vec<T> или для любых других обобщенных контейнеров. Для таких обобщенных случаев необходимо использовать fmt::Debug.

Однако это не проблема, потому что для любого нового контейнерного типа, который не является обобщенным, можно реализовать fmt::Display.

use std::fmt; // Импортируем `fmt`

// Структура, которая хранит два числа. Будет получен `Debug` для того, чтобы
// результаты можно было сравнить с `Display`.
#[derive(Debug)]
struct MinMax(i64, i64);

// Реализуем `Display` для `MinMax`.
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,
}

// Аналогично реализуем `Display` для `Point2D`.
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 есть много таких трейтов, и каждый требует собственной реализации. Подробнее об этом описано в std::fmt.

Практическое задание

После проверки вывода примера выше, используйте структуру Point2D в качестве руководства для добавления структуры Complex в пример. При выводе аналогично предыдущему примеру, вывод должен быть таким:

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

Резюме

Поздравляем! Вы завершили практическое занятие по Display. Вы можете выполнить больше практических заданий в LabEx, чтобы улучшить свои навыки.