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).

Шпаргалка

Предложение 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
}
quiz iconПроверьте себя

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

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