Введение
В этом практическом занятии объясняется функциональность форматированного вывода в 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, чтобы улучшить свои навыки.