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

Макросы вывода

Функция печати (вывода) в Rust реализована набором макросов из модуля std::fmt. Рассмотрим популярные макросы:

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

Все макросы с окончанием ln добавляют символ перевода строки, после вывода курсор переходит на новую строку.

Префикс e — сокращение от error, используется для вывода сообщений об ошибках

Все перечисленные макросы используют одинаковый синтаксис форматирования, и Rust проверяет корректность кода форматирования на этапе компиляции.

Примеры использования

Базовый заполнитель {}

{} — универсальный заполнитель, последовательно заменяется переданными аргументами, автоматически преобразуясь в строку для вывода.

fn main() {
    println!("{} days", 31);
    // Вывод:31 days
}Code language: JavaScript (javascript)

Позиционные аргументы

Внутри {} указывается числовой индекс (отсчет с 0), чтобы вручную выбрать нужный аргумент; поддерживается повторное использование и перестановка порядка аргументов.

fn main() {
    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");
    // Вывод:Alice, this is Bob. Bob, this is Alice
}Code language: JavaScript (javascript)

Как видно из примера, индексы аргументов не обязаны идти по порядку 0 1 2…, их можно переставлять. Переданные аргументы «Alice», «Bob» имеют индексы 0 и 1 соответственно.

Именованные аргументы

Если работа с индексами кажется неочевидной, используйте «именованные аргументы»: в форматирующей строке пишите {имя_аргумента}, при вызове передавайте значения в формате имя_аргумента=значение. Порядок передачи не важен, читаемость кода выше.

fn main() {
    println!("{subject} {verb} {object}",
         object="the lazy dog",
         subject="the quick brown fox",
         verb="jumps over");
    // Вывод:the quick brown fox jumps over the lazy dog
}Code language: JavaScript (javascript)

По сути вы присваиваете имена передаваемым значениям, после чего в форматирующей строке обращаетесь к ним через {имя_аргумента} — это гораздо нагляднее.

Форматирование систем счисления(:спецификатор

После двоеточия : внутри заполнителя указывается символ формата, чтобы преобразовать число в двоичную, восьмеричную, шестнадцатеричную и другие системы счисления.

  • :b:двоичная система
  • :o:восьмеричная система
  • :x:шестнадцатеричная система (маленькие буквы)
fn main() {
    println!("Десятичная:    {}",   69420);    // 69420
    println!("Двоичная:  {:b}", 69420);    // 10000111100101100
    println!("Восьмеричная:  {:o}", 69420);    // 207454
    println!("Шестнадцатеричная:{:x}", 69420);    // 10f2c
}Code language: JavaScript (javascript)

Ширина, выравнивание и заполнение

1 Выравнивание вправо(>ширина

{:>n} означает общую ширину вывода n, содержимое выравнивается вправо, свободное пространство слева заполняется пробелами.

fn main() {
    println!("{number:>5}", number=1);
    // Общая ширина 5, слева 4 пробела → Вывод:    1
}Code language: JavaScript (javascript)
2 Выравнивание с заполнением нулями

Перед символом выравнивания добавьте 0, чтобы заполнять свободное место цифрой 0 вместо пробелов.

  • :0>5:выравнивание вправо, заполнение нулями слева
  • :0<5:выравнивание влево, заполнение нулями справа
fn main() {
    println!("{number:0>5}", number=1); // Выравнивание вправо, нули слева → 00001
    println!("{number:0<5}", number=1); // Выравнивание влево, нули справа → 10000
}Code language: JavaScript (javascript)

Можно использовать любой другой символ вместо нуля. В примере ниже свободное место слева заполняется большой буквой A

fn main() {
    println!("{number:A>5}", number=1); // Выравнивание вправо, заполнение A → AAAA1
    println!("{number:A<5}", number=1); // Выравнивание влево, заполнение A → 1AAAA
}Code language: PHP (php)
3 Динамическая ширина(имя_переменной$

Ширина не прописывается жестко в коде, через имя_переменной$ ссылаемся на внешние именованные аргументы / переменные для динамического регулирования ширины вывода.

То есть ширину можно задавать через переменную, а не фиксировать прямо в коде.

fn main() {
    // Способ 1: совместно с именованными аргументами
    println!("{number:0>width$}", number=1, width=8);

    // Способ 2: прямое обращение к переменным внутри кода (поддерживается Rust 1.58+)
    let number: f64 = 1.0;
    let width: usize = 8;
    println!("{number:>width$}"); // Вывод:       1
}Code language: JavaScript (javascript)

Проверка аргументов на этапе компиляции

Rust на этапе компиляции проверяет совпадение количества заполнителей и переданных аргументов; при нехватке аргументов сразу выдаёт ошибку компиляции.

fn main() {
    // 2 заполнителя, но передан только 1 аргумент → компиляция прерывается
    println!("My name is {0}, {1} {0}", "Bond");
    // Исправление: добавить аргумент "James"
    println!("My name is {0}, {1} {0}", "Bond", "James");
}Code language: JavaScript (javascript)

Сообщение компилятора: позиция {1} невалидна, потому что передан лишь один аргумент.

Ошибка: ссылка на несуществующий позиционный аргумент 1 (передан только 1 аргумент)
 --> D:\rustdemo\hello.rs:3:29
  |
3 |     println!("My name is {0}, {1} {0}", "Bond");
  |                                ^
  |
  = Подсказка: позиционные аргументы нумеруются с 0

Ошибка: компиляция остановлена, всего 1 ошибкаCode language: JavaScript (javascript)

Ограничения форматирования пользовательских типов

По умолчанию конструкция {} работает только с типами, реализующими трейт fmt::Display. Пользовательские структуры этот трейт не реализуют из коробки, прямой вывод через {} вызовет ошибку компиляции.

fn main() {
    struct Structure(i32);
    // Следующий код не скомпилируется
    println!("{}", Structure(3));
}Code language: JavaScript (javascript)
Ошибка[E0277]: структура `Structure` не реализует трейт `std::fmt::Display`
 --> D:\rustdemo\hello.rs:4:20
4 |     println!("{}", Structure(3));
  |               --   ^^^^^^^^^^^^ этот тип не может быть выведен стандартным форматтером
  |               |
  |               этот форматный аргумент требует реализации трейта Display для типа

Подсказка: у структуры `Structure` нет реализации `std::fmt::Display`
 --> D:\rustdemo\hello.rs:2:2
2 |     struct Structure(i32);
  |     ^^^^^^^^^^^^^^^^

Дополнение: в форматирующей строке попробуйте использовать `{:?}` (или `{:#?}` для красивого отладочного вывода)
Компиляция остановлена, найдена 1 ошибка.

Для детализации ошибки выполните команду: rustc --explain E0277Code language: JavaScript (javascript)

* 1 Если нужно вывести структуру — требуется реализовать трейт Display (пока достаточно просто запомнить, разберем позже)

use std::fmt;

struct Structure(i32);

// Реализуем трейт Display для структуры
impl fmt::Display for Structure {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // self.0 обращается к значению i32 внутри кортежной структуры
        write!(f, "Число:{}", self.0)
    }
}

fn main() {
    println!("{}", Structure(3));
}Code language: PHP (php)
* 2 Временная отладка → отладочный формат {:?} / {:#?}

Подходит для отладки разработчика: если не продакшен окружение и нужно просто посмотреть значения внутри структуры, используйте отладочный трейт Debug

Rust по умолчанию реализует отладочный трейт Debug для пользовательских типов, вывод осуществляется через {:?}:

#[derive(Debug)]
#[allow(dead_code)] // убрать предупреждения о неиспользуемом коде/полях
struct Structure(i32);

fn main() {
    println!("{:?}", Structure(3));
    println!("{:#?}", Structure(3));
}Code language: PHP (php)

* Два ключевых форматирующих трейта модуля std::fmt

Модуль std::fmt содержит множество трейтов для управления отображением текста, два основных:

  1. fmt::Debug используется с заполнителем {:?}, предназначен исключительно для вывода в отладочных целях.
  2. fmt::Display используется с заполнителем {}, выводит контент в стандартном, удобном для обычных пользователей виде.

Встроенные типы стандартной библиотеки уже имеют реализацию обоих трейтов и выводятся напрямую; пользовательские типы не работают без ручной реализации.

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

#[allow(dead_code)]

#[allow(dead_code)] — атрибут кода, действующий на следующий за ним элемент кода, нужен для скрытия предупреждений «неиспользуемый код».

Рассмотрим пример выше

fn main() {
    struct Structure(i32);
    // Следующий код не скомпилируется
    //println!("{}", Structure(3));
}Code language: JavaScript (javascript)

Строка println!(«{}», Structure(3)); вызывает ошибку, поэтому закомментирована. При запуске такого кода мы получим:

Предупреждение: структура `Structure` никогда не создаётся
 --> D:\rustdemo\hello.rs:2:12
  |
2 |     struct Structure(i32);
  |            ^^^^^^^^^
  |
  = примечание: `#&#91;warn(dead_code)]` (часть `#&#91;warn(unused)]`) включено по умолчанию

Выдано 1 предупреждение
Code language: JavaScript (javascript)

Такое предупреждение появляется, потому что строка struct Structure(i32); нигде не используется. Чтобы скрыть предупреждение, добавьте данный атрибут

fn main() {
    #[allow(dead_code)] 
    struct Structure(i32);
    // Следующий код не скомпилируется
    //println!("{}", Structure(3));
}Code language: PHP (php)
Дробные числа: указание количества знаков после запятой

Определите переменную pi = 3.141592, напишите форматированный вывод с сохранением трех знаков после запятой, вывод:Pi is roughly 3.142.

fn main() {
    let pi = 3.141592;
    println!("Pi is roughly {:.3}", pi);//3.142
    println!("Pi is roughly {0}", pi); //3.141592
}Code language: JavaScript (javascript)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *