XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Итак, fmt::Display был реализован, но fmt::Binary нет, следовательно не может быть использован. std::fmt имеет много таких типажей и каждый из них требует свою реализацию. Это более подробно описано в документации к std::fmt.
После того, как запустите код, представленный выше, используйте структуру Point2D как пример и добавьте новую структуру Complex, чтобы вывод был таким:
Display: 3.3 +7.2i
Debug: Complex { reaclass="underline" 3.3, imag: 7.2 }
derive, std::fmt, макросы, struct, trait и use
Реализовать fmt::Display для структуры, в которой каждый элемент должен обрабатываться последовательно не так то просто. Проблема в том, что write! каждый раз возвращает fmt::Result. Для правильного обращения с этим необходимо обрабатывать все результаты. Для этой цели Rust предоставляет оператор ?.
Использование ? для write! выглядит следующим образом:
// Попробуй исполнить `write!`, чтобы узнать, вернется ли ошибка. Если ошибка, верни её.
// Если нет, то продолжи.
write!(f, "{}", value)?;
С помощью оператора ? реализация fmt::Display для Vec довольно простая:
use std::fmt; // Импортируем модуль `fmt`.
// Определим структуру с именем `List`, которая хранит в себе `Vec`.
struct List(Vec<i32>);
impl fmt::Display for List {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Получаем значение с помощью индекса кортежа
// и создаём ссылку на `vec`.
let vec = &self.0;
write!(f, "[")?;
// Пройдёмся по каждому `v` в `vec`.
// Номер итерации хранится в `count`.
for (count, v) in vec.iter().enumerate() {
// Для каждого элемента, кроме первого, добавим запятую
// до вызова `write!`. Используем оператор `?` или `try!`,
// чтобы вернуться при наличие ошибок.
if count != 0 { write!(f, ", ")?; }
write!(f, "{}", v)?;
}
// Закроем открытую скобку и вернём значение `fmt::Result`
write!(f, "]")
}
}
fn main() {
let v = List(vec![1, 2, 3]);
println!("{}", v);
}
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Попробуйте изменить программу так, чтобы индекс элемента так же выводился в консоль. Новый вывод должен выглядеть примерно вот так:
[0: 1, 1: 2, 2: 3]
for, ref, Result, struct, ?, и vec!
Мы видели, что форматирование задаётся макросом форматирования:
• format!("{}", foo) -> "3735928559"
• format!("0x{:X}", foo) ->"0xDEADBEEF"
• format!("0o{:o}", foo) -> "0o33653337357"
Одна и та же переменная (foo) может быть отображена по разному в зависимости от используемого типа аргумента: X, o или неопределённый.
Функционал форматирования реализован благодаря типажу, и для каждого типа аргумента существует свой. Наиболее распространённый типаж для форматирования - Display, который работает без аргументов: {}, например.
use std::fmt::{self, Formatter, Display};
struct City {
name: &'static str,
// Широта
lat: f32,
// Долгота
lon: f32,
}
impl Display for City {
// `f` - это буфер, данный метод должен записать в него форматированную строку
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' };
let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' };
// `write!` похож на `format!`, только он запишет форматированную строку