Menu
Coddy logo textTech

Возврат типов с трейтами

Часть раздела Объектно-ориентированное программирование путешествия по Rust на Coddy. Урок 43 из 61.

Вы использовали ограничения трейтов, чтобы ограничить типы, которые может принимать function. Но как насчёт того, что function возвращает? Синтаксис impl Trait позволяет указать, что function возвращает «некоторый тип, реализующий этот трейт», не называя конкретный тип.

Вот синтаксис в действии:

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

struct Article {
    headline: String,
}

impl Summary for Article {
    fn summarize(&self) -> String {
        self.headline.clone()
    }
}

fn create_summary() -> impl Summary {
    Article { headline: String::from("Breaking News!") }
}

Тип возвращаемого значения impl Summary сообщает вызывающим: «вы получите что-то, что implements Summary». Вызывающий может использовать любой method из трейта, но need знать, что фактический тип — это Article.

Это особенно полезно, когда тип concrete сложный или когда вы хотите скрыть детали реализации. function обещает возможность (трейт), а не конкретный тип.

Однако есть важное ограничение: function должна вернуть ровно один concrete тип. Нельзя условно возвращать разные типы, которые implements один и тот же трейт. Для этого требуются объекты трейтов, о которых ты узнаешь позже.

// Это работает — всегда возвращает Article
fn make_item() -> impl Summary {
    Article { headline: String::from("Hello") }
}

// Это бы НЕ скомпилировалось — два разных типа
// fn make_item(flag: bool) -> impl Summary {
//     if flag { Article { ... } } else { Tweet { ... } }
// }
challenge icon

Задание

Легко

Давайте создадим фабрику поздравительных открыток, которая использует возвращаемый тип impl Trait, чтобы скрыть детали реализации! Вы создадите function, которая возвращает «что-то, способное приветствовать», не раскрывая конкретный тип вызывающему коду.

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

  • greetings.rs: Определите публичный трейт Greet с greet method, который принимает &self и возвращает String. Затем создайте структуру Card (она не обязана быть публичной!) с полем message (String). Реализуйте трейт Greet для Card, возвращая сообщение. Наконец, создайте публичную function с именем create_greeting, которая принимает параметр типа String и возвращает impl Greet. Эта function должна создать и вернуть Card с переданным сообщением.
  • main.rs: Подключите модуль greetings и используйте предоставленные входные данные, чтобы вызвать create_greeting. В этом и заключается преимущество: ваш главный файл вообще ничего не знает о типе Card. Он знает только, что получил нечто, что реализует Greet. Вызовите greet method у возвращённого значения и выведите result.

Главная идея заключается в том, что Card остаётся приватным для модуля, но вызывающий код всё ещё может использовать его через интерфейс трейта. function обещает возможность (приветствие), а не конкретный тип.

Ваш вывод должен отображать приветствие:

{message}

Например, при вводе Happy Birthday!:

Happy Birthday!

А при вводе Congratulations on your promotion!:

Congratulations on your promotion!

Вы получите одни входные данные: сообщение-приветствие.

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

mod greetings;

use greetings::Greet;

fn main() {
    // Прочитать приветственное сообщение из ввода
    let mut input = String::new();
    std::io::stdin().read_line(&mut input).expect("Failed to read line");
    let message = input.trim().to_string();

    // TODO: Вызовите create_greeting с сообщением
    // Note: Вы не знаете конкретный тип — только то, что он реализует Greet!
    
    // TODO: Вызовите метод greet и выведите результат
}
quiz iconПроверьте себя

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

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

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