Форматированный вывод в Rust

Beginner

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

Введение

В этом практическом занятии объясняется функциональность форматированного вывода в Rust. Модуль std::fmt предоставляет макросы, такие как format!, print!, println!, eprint! и eprintln!, для обработки задач вывода. Эти макросы позволяют форматировать текст с использованием占位符,которые заменяются соответствующими аргументами. Можно использовать позиционные и именованные аргументы, и различный формат можно применить с использованием символов форматирования. Макросы также поддерживают выравнивание текста, дополнение чисел и установку точности для десятичных чисел. Трейт fmt::Display используется для форматирования текста в удобочитаемом виде, в то время как трейт fmt::Debug используется для целей отладки. Rust также проверяет правильность форматирования на этапе компиляции. Кроме того, упоминается, что реализация трейта fmt::Display автоматически реализует трейт ToString, и для пользовательских типов необходимо реализовать трейт fmt::Display, чтобы они были печатаемыми. В практическом занятии также включены задания для практики использования макросов и трейтов форматированного вывода.

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

Форматированный вывод

Вывод осуществляется с помощью серии макросов, определенных в std::fmt, некоторые из которых включают:

  • format!: записывает отформатированный текст в String.
  • print!: то же, что и format!, но текст выводится в консоль (io::stdout).
  • println!: то же, что и print!, но в конец добавляется новая строка.
  • eprint!: то же, что и print!, но текст выводится в стандартный поток ошибок (io::stderr).
  • eprintln!: то же, что и eprint!, но в конец добавляется новая строка.

Все они разбирают текст одинаково. Кроме того, Rust проверяет правильность форматирования на этапе компиляции.

fn main() {
    // В общем, `{}` будет автоматически заменено любым
    // аргументом. Эти аргументы будут приведены к строке.
    println!("{} days", 31);

    // Можно использовать позиционные аргументы. Указание целого числа внутри `{}`
    // определяет, какой дополнительный аргумент будет заменен. Аргументы начинаются
    // с 0 сразу после строки форматирования.
    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");

    // Также можно использовать именованные аргументы.
    println!("{subject} {verb} {object}",
             object="the lazy dog",
             subject="the quick brown fox",
             verb="jumps over");

    // Различный формат можно вызвать, указав символ форматирования
    // после `:`.
    println!("Base 10:               {}",   69420); // 69420
    println!("Base 2 (binary):       {:b}", 69420); // 10000111100101100
    println!("Base 8 (octal):        {:o}", 69420); // 207454
    println!("Base 16 (hexadecimal): {:x}", 69420); // 10f2c
    println!("Base 16 (hexadecimal): {:X}", 69420); // 10F2C

    // Можно выровнять текст по правому краю с указанной шириной. Это будет
    // выводить "    1". (Четыре пробела и "1", общая ширина 5.)
    println!("{number:>5}", number=1);

    // Можно дополнить числа дополнительными нулями,
    println!("{number:0>5}", number=1); // 00001
    // и выровнять по левому краю, изменив знак. Это будет выводить "10000".
    println!("{number:0<5}", number=1); // 10000

    // Можно использовать именованные аргументы в спецификаторе формата, добавив `$`.
    println!("{number:0>width$}", number=1, width=5);

    // Rust даже проверяет, чтобы количество аргументов было правильным.
    println!("My name is {0}, {1} {0}", "Bond");
    // FIXME ^ Добавьте отсутствующий аргумент: "James"

    // Только типы, реализующие fmt::Display, могут быть отформатированы с помощью `{}`. Пользовательские
    // типы по умолчанию не реализуют fmt::Display.

    #[allow(dead_code)] // отключает предупреждение `dead_code` о неиспользуемом модуле
    struct Structure(i32);

    // Это не скомпилируется, потому что `Structure` не реализует
    // fmt::Display.
    // println!("This struct `{}` won't print...", Structure(3));
    // TODO ^ Попробуйте раскомментировать эту строку

    // Для Rust 1.58 и выше можно напрямую захватывать аргумент из окружающей переменной.
    // Как и выше, это будет выводить "    1", 4 пробела и "1".
    let number: f64 = 1.0;
    let width: usize = 5;
    println!("{number:>width$}");
}

std::fmt содержит множество трейтов, которые определяют отображение текста. Основные формы двух важных из них перечислены ниже:

  • fmt::Debug: использует маркер {:?}. Форматирует текст для целей отладки.
  • fmt::Display: использует маркер {}. Форматирует текст более элегантно и удобочитаемо для пользователя.

Здесь мы использовали fmt::Display, потому что стандартная библиотека предоставляет реализации для этих типов. Чтобы вывести текст для пользовательских типов, требуются дополнительные шаги.

Реализация трейта fmt::Display автоматически реализует трейт ToString, который позволяет нам преобразовать тип в String.

В строке 43 #[allow(dead_code)] является [атрибутом], который применяется только к модулю, расположенному после него.

Задания

  • Исправьте проблему в коде выше (см. FIXME), чтобы он работал без ошибок.
  • Попробуйте раскомментировать строку, которая пытается отформатировать структуру Structure (см. TODO)
  • Добавьте вызов макроса println!, который выводит: Pi is roughly 3.142, контролируя количество десятичных знаков. В рамках этого упражнения используйте let pi = 3.141592 в качестве оценки числа Пи. (Совет: вам может потребоваться проверить документацию по std::fmt для настройки количества отображаемых десятичных знаков)

Резюме

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