Конструкция 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).
Шпаргалка
Предложение where предоставляет более чистый способ указания ограничений типажа (trait bounds) для универсальных функций, перенося ограничения после списка параметров:
fn complex_operation<T, U>(first: T, second: U)
where
T: Display + Clone,
U: Debug + Summary,
{
// ...
}
Это функционально идентично встроенным ограничениям типажа, но улучшает читаемость:
fn complex_operation<T: Display + Clone, U: Debug + Summary>(first: T, second: U) {
// ...
}
Предложение where особенно полезно, когда:
- У вас есть несколько универсальных параметров
- Каждый параметр требует нескольких ограничений типажа
- Вы хотите сохранить сигнатуру функции чистой и легко читаемой
Требования каждого типа отображаются на отдельной строке, что позволяет легко увидеть, что требуется каждому универсальному параметру. Это предпочтительный стиль в большинстве кодовых баз Rust, когда ограничения выходят за рамки простого T: Trait.
Попробуйте сами
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