Конструкция where
Часть раздела Объектно-ориентированное программирование путешествия по 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,
{
// ...
}
Обе версии функционально идентичны: clause where предназначен исключительно для удобства чтения. Требования каждого типа указаны в отдельной строке, поэтому легко просмотреть, что требуется каждому generic-параметру.
Предложение where становится особенно полезным, когда ограничения сложные или когда у вас много generic-параметров. Оно позволяет сразу видеть имя function и параметры, а ограничения перечисляются отдельно ниже. Это предпочтительный стиль в большинстве кодовых баз Rust, когда ограничения выходят за рамки простого T: Trait.
Задание
ЛегкоДавайте создадим систему обработки данных, которая использует предложение where, чтобы сохранить читаемость сложных ограничений трейтов! Вы создадите generic функцию с несколькими параметрами типов, для каждого из которых требуются разные трейты, и аккуратно организуете ограничения с помощью синтаксиса where.
Вы организуете свой код в двух файлах:
processor.rs: Define два public трейта и generic функцию, которая использует их оба:Summarize: трейт сsummaryметодом, который принимает&selfи возвращаетStringValidate: трейт сvalidateметодом, который принимает&selfи возвращаетbool
process_items, которая принимает два параметра разных generic типов. Используйте предложениеwhere, чтобы указать, что первый тип должен implement оба трейта —CloneиSummarize, тогда как второй тип должен implementValidate. Функция должна вывести summary первого item, а затем вывести, является ли второй item корректным.main.rs: Создайте две public структуры, которые implement требуемые трейты:Article: с public полемtitle(String). ImplementSummarize, чтобы возвращатьArticle: {title}, и deriveClone.Form: с public полемfilled(bool). ImplementValidate, чтобы возвращать значениеfilled.
process_itemsс ними обоими.
Предложение where делает сигнатуру вашей функции гораздо чище, чем размещение всех ограничений внутри угловых скобок. Требования каждого типа находятся на отдельной строке, поэтому легко увидеть, что нужно каждому generic параметру.
Ваш вывод должен содержать summary, за которым следует результат проверки:
Article: {title}
Valid: {true/false}Например, для входных данных Rust Tips и true:
Article: Rust Tips
Valid: trueА для входных данных Breaking News и false:
Article: Breaking News
Valid: falseВы получите два входных значения: заголовок статьи и информацию о том, заполнена ли форма (выполните parse как bool).
Попробуйте сами
mod processor;
use processor::{Summarize, Validate, process_items};
use std::io;
// TODO: Определите публичную структуру Article с публичным полем title (String)
// Добавьте Derive 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 с заданным title
// TODO: Создайте Form с заданным значением filled
// TODO: Вызовите process_items с article и form
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media PlayerПотренируйтесь самостоятельно: Онлайн-компилятор Rust