Конструкция where
Часть раздела Object Oriented Programming путешествия по Rust на Coddy. Урок 42 из 61.
По мере добавления новых ограничений типажей, сигнатуры функций могут стать трудными для чтения. Рассмотрим функцию с несколькими обобщёнными параметрами, каждый из которых требует нескольких типажей:
fn complex_operation<T: Display + Clone, U: Debug + Summary>(first: T, second: U) {
// ...
}
Это работает, но сигнатура становится перегруженной. Предложение where предлагает более чистую альтернативу, перенося ограничения типажа после списка параметров:
fn complex_operation<T, U>(first: T, second: U)
where
T: Display + Clone,
U: Debug + Summary,
{
// ...
}
Обе версии функционально идентичны — предложение where используется исключительно для улучшения читаемости. Требования каждого типа располагаются на отдельной строке, что позволяет легко увидеть, в чем нуждается каждый универсальный параметр.
Предложение where становится особенно ценным, когда ограничения сложны или когда у вас много универсальных параметров. Оно позволяет имени функции и параметрам оставаться на виду, а ограничения перечисляются отдельно ниже. Это предпочтительный стиль в большинстве кодовых баз Rust, когда ограничения выходят за рамки простого T: Trait.
Задание
ЛегкоДавайте создадим систему обработки данных, которая использует предложение where для сохранения читаемости сложных ограничений типажей! Вы создадите универсальную функцию с несколькими параметрами типа, каждый из которых требует различных типажей, и аккуратно организуете ограничения, используя синтаксис where.
Вы организуете свой код в двух файлах:
processor.rs: Определите два публичных типажа и универсальную функцию, которая использует их оба:Summarize— типаж с методомsummary, который принимает&selfи возвращаетStringValidate— типаж с методомvalidate, который принимает&selfи возвращаетbool
process_items, которая принимает два параметра разных универсальных типов. Используйте предложениеwhere, чтобы указать, что первый тип должен реализовывать какClone, так иSummarize, в то время как второй тип должен реализовыватьValidate. Функция должна вывести сводку (summary) первого элемента, а затем вывести, является ли второй элемент валидным.main.rs: Создайте две публичные структуры, которые реализуют необходимые типажи:Article— с публичным полемtitle(String). РеализуйтеSummarize, чтобы он возвращал"Article: {}"с заголовком, и добавьте производную реализацию (derive)Clone.Form— с публичным полемfilled(bool). РеализуйтеValidate, чтобы он возвращал значениеfilled.
process_itemsс обоими объектами.
Предложение where делает сигнатуру вашей функции гораздо чище, чем если бы вы втискивали все ограничения в угловые скобки. Требования каждого типа отображаются на отдельной строке, что позволяет легко увидеть, что нужно каждому универсальному параметру.
Ваш вывод должен содержать сводку, за которой следует результат валидации:
Article: {title}
Valid: {true/false}Например, с входными данными "Rust Tips" и "true":
Article: Rust Tips
Valid: trueИ с входными данными "Breaking News" и "false":
Article: Breaking News
Valid: falseВы получите два входных значения: заголовок статьи и информацию о том, заполнена ли форма (преобразуйте в bool).
Попробуйте сами
mod processor;
use processor::{Summarize, Validate, process_items};
use std::io;
// TODO: Определите публичную структуру Article с публичным полем title (String)
// Реализуйте Clone для Article
// TODO: Реализуйте Summarize для Article
// Метод summary должен возвращать "Article: {title}"
// TODO: Определите публичную структуру Form с публичным полем filled (bool)
// TODO: Реализуйте Validate для Form
// Метод validate должен возвращать значение filled
fn main() {
let mut title = String::new();
io::stdin().read_line(&mut title).expect("Failed to read line");
let title = title.trim().to_string();
let mut filled_input = String::new();
io::stdin().read_line(&mut filled_input).expect("Failed to read line");
let filled: bool = filled_input.trim().parse().expect("Failed to parse bool");
// TODO: Создайте Article с заданным заголовком
// TODO: Создайте Form с заданным значением filled
// TODO: Вызовите process_items с article и form
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media PlayerПотренируйтесь самостоятельно: Онлайн-компилятор Rust