Menu
Coddy logo textTech

Множественные ограничения

Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 41 из 61.

Иногда одного ограничения типажа недостаточно. Вам может понадобиться обобщённый тип, который можно и вывести на печать, и получить по нему сводку. Rust позволяет требовать несколько типажей, используя синтаксис +.

Вот как указать, что тип должен реализовывать два типажа:

use std::fmt::Display;

trait Summary {
    fn summarize(&self) -> String;
}

fn announce<T: Display + Summary>(item: T) {
    println!("Breaking news: {}", item);
    println!("Summary: {}", item.summarize());
}

Ограничение T: Display + Summary означает, что «T должен реализовывать как Display, так и Summary». Внутри функции вы можете использовать возможности обоих типажей — вывод с помощью {} (из Display) и вызов summarize() (из Summary).

Вы можете объединять в цепочку столько типажей, сколько необходимо:

fn process<T: Display + Summary + Clone>(item: T) {
    // Можно выводить на печать, суммировать И клонировать
}

Этот паттерн необходим, когда ваша функция полагается на несколько вариантов поведения. Вместо того чтобы принимать любой тип и надеяться, что он сработает, вы явно объявляете, какие именно возможности требуются, — и компилятор проверяет это на этапе компиляции.

challenge icon

Задание

Легко

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

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

  • product.rs: Определите публичный типаж Inspectable с методом inspect, который принимает &self и возвращает String, содержащую детали инспекции. Затем создайте публичную структуру Gadget с публичными полями name (String) и serial (u32). Ваш Gadget должен реализовывать как Inspectable (возвращая "Inspecting: {}" с именем), так и std::fmt::Display (форматируя как "{} (SN: {})"). Наконец, создайте публичную универсальную функцию full_report, которая принимает любой тип T, реализующий одновременно Display и Inspectable. Эта функция должна выводить две строки: сначала сам предмет, используя форматтер {}, затем результат вызова inspect().
  • main.rs: Подключите модуль product и создайте экземпляр Gadget, используя предоставленные входные данные. Вызовите full_report со своим гаджетом, чтобы показать, что он удовлетворяет требованиям обоих типажей.

Сила множественных ограничений заключается в том, что ваша функция full_report может использовать возможности обоих типажей — красиво отображать предмет И получать детали инспекции — и все это гарантируется на этапе компиляции.

Ваш вывод должен показывать как формат отображения, так и результат инспекции:

{name} (SN: {serial})
Inspecting: {name}

Например, при входных данных Smartwatch и 98765:

Smartwatch (SN: 98765)
Inspecting: Smartwatch

Вы получите два входных значения: название гаджета и серийный номер (преобразуйте в u32).

Шпаргалка

Вы можете указать несколько ограничений типажа (trait bounds) для универсального типа, используя синтаксис +:

fn function_name<T: Trait1 + Trait2>(item: T) {
    // Можно использовать возможности обоих типажей
}

Пример с Display и пользовательским типажом:

use std::fmt::Display;

trait Summary {
    fn summarize(&self) -> String;
}

fn announce<T: Display + Summary>(item: T) {
    println!("Breaking news: {}", item);
    println!("Summary: {}", item.summarize());
}

Вы можете объединять в цепочку столько типажей, сколько необходимо:

fn process<T: Display + Summary + Clone>(item: T) {
    // Можно выводить на печать, суммировать И клонировать
}

Компилятор гарантирует, что тип реализует все указанные типажи на этапе компиляции.

Попробуйте сами

mod product;

use product::{Gadget, full_report};

fn main() {
    // Чтение входных данных
    let mut name = String::new();
    std::io::stdin().read_line(&mut name).expect("Failed to read line");
    let name = name.trim().to_string();
    
    let mut serial_input = String::new();
    std::io::stdin().read_line(&mut serial_input).expect("Failed to read line");
    let serial: u32 = serial_input.trim().parse().expect("Failed to parse serial");
    
    // TODO: Создайте экземпляр Gadget с именем и серийным номером
    
    // TODO: Вызовите full_report с вашим гаджетом
}
quiz iconПроверьте себя

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

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