Menu
Coddy logo textTech

Динамическая диспетчеризация

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

Когда вы используете обобщённые типы с ограничениями типажа, такими как <T: Summary>, Rust определяет конкретный тип во время компиляции. Это называется статической диспетчеризацией — компилятор генерирует специализированный код для каждого конкретного типа, который вы используете. Это работает быстро, но есть ограничение: переменная может хранить только один конкретный тип.

Что если вам нужна одна переменная, которая могла бы хранить разные типы во время выполнения? Здесь на помощь приходят объекты типажа. Используя Box<dyn Trait>, вы можете хранить любой тип, реализующий этот типаж:

trait Speak {
    fn speak(&self) -> String;
}

struct Dog;
struct Cat;

impl Speak for Dog {
    fn speak(&self) -> String { String::from("Woof!") }
}

impl Speak for Cat {
    fn speak(&self) -> String { String::from("Meow!") }
}

fn main() {
    let animal: Box<dyn Speak> = Box::new(Dog);
    println!("{}", animal.speak());  // "Woof!"
    
    let animal: Box<dyn Speak> = Box::new(Cat);
    println!("{}", animal.speak());  // "Meow!"
}

Ключевое слово dyn указывает на динамическую диспетчеризацию — Rust определяет, какой метод вызвать, во время выполнения, а не во время компиляции. Box необходим, потому что объекты-типажи не имеют известного размера; Box предоставляет указатель фиксированного размера.

Подумайте об этом так: обобщения говорят «Я работаю с типом T», в то время как объекты-типажи говорят «Я работаю с чем угодно, что может это делать». Платой за это является небольшая стоимость времени выполнения в обмен на гибкость обработки различных типов через одну и ту же переменную.

challenge icon

Задание

Легко

Давайте создадим систему проката транспортных средств, которая продемонстрирует мощь динамической диспетчеризации! Вы создадите типаж (trait), который определяет, что может делать любое транспортное средство, доступное для аренды, а затем используете Box<dyn Trait> для хранения различных типов транспортных средств в одной и той же переменной.

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

  • vehicle.rs: Определите публичный типаж Rentable с методом rental_info, который принимает &self и возвращает String. Затем создайте две публичные структуры:
    • Car — с публичным полем model (String). Его метод rental_info должен возвращать "Car: {model}"
    • Bike — с публичным полем brand (String). Его метод rental_info должен возвращать "Bike: {brand}"
  • main.rs: Подключите модуль vehicle и используйте входные данные для демонстрации динамической диспетчеризации. Создайте переменную типа Box<dyn Rentable>, которая сначала хранит Car, выведите информацию о его аренде, затем переназначьте ту же переменную для хранения Bike и снова выведите информацию об аренде.

Магия здесь заключается в том, что одна переменная типа Box<dyn Rentable> может хранить либо Car, либо Bike — Rust определяет, какой метод rental_info вызывать во время выполнения. Это и есть динамическая диспетчеризация в действии!

Ваш вывод должен содержать информацию об обоих транспортных средствах:

Car: {model}
Bike: {brand}

Например, при входных данных "Tesla Model 3" и "Trek":

Car: Tesla Model 3
Bike: Trek

Вы получите два входных значения: модель автомобиля и марку велосипеда.

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

mod vehicle;

use vehicle::{Rentable, Car, Bike};

fn main() {
    // Чтение ввода
    let mut car_model = String::new();
    std::io::stdin().read_line(&mut car_model).expect("Failed to read line");
    let car_model = car_model.trim().to_string();
    
    let mut bike_brand = String::new();
    std::io::stdin().read_line(&mut bike_brand).expect("Failed to read line");
    let bike_brand = bike_brand.trim().to_string();
    
    // TODO: Создайте переменную типа Box<dyn Rentable>, которая содержит Car
    // Выведите информацию об аренде, используя метод rental_info()
    
    // TODO: Переназначьте ту же переменную, чтобы она содержала Bike
    // Снова выведите информацию об аренде
    
}
quiz iconПроверьте себя

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

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