Menu
Coddy logo textTech

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

challenge icon

Задание

Легко

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

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

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

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

Все уроки раздела Объектно-ориентированное программирование

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