Выбрать главу

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!`, только он запишет форматированную строку