Menu
Coddy logo textTech

Конструкция 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.

challenge icon

Задание

Легко

Давайте создадим систему обработки данных, которая использует предложение where для сохранения читаемости сложных ограничений типажей! Вы создадите универсальную функцию с несколькими параметрами типа, каждый из которых требует различных типажей, и аккуратно организуете ограничения, используя синтаксис where.

Вы организуете свой код в двух файлах:

  • processor.rs: Определите два публичных типажа и универсальную функцию, которая использует их оба:
    • Summarize — типаж с методом summary, который принимает &self и возвращает String
    • Validate — типаж с методом validate, который принимает &self и возвращает bool
    Затем создайте публичную универсальную функцию с именем process_items, которая принимает два параметра разных универсальных типов. Используйте предложение where, чтобы указать, что первый тип должен реализовывать как Clone, так и Summarize, в то время как второй тип должен реализовывать Validate. Функция должна вывести сводку (summary) первого элемента, а затем вывести, является ли второй элемент валидным.
  • main.rs: Создайте две публичные структуры, которые реализуют необходимые типажи:
    • Article — с публичным полем title (String). Реализуйте Summarize, чтобы он возвращал "Article: {}" с заголовком, и добавьте производную реализацию (derive) Clone.
    • Form — с публичным полем filled (bool). Реализуйте Validate, чтобы он возвращал значение filled.
    Используйте предоставленные входные данные для создания Article и Form, затем вызовите 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
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming

Потренируйтесь самостоятельно: Онлайн-компилятор Rust