Menu
Coddy logo textTech

Dynamiczne wywołanie

Część sekcji Programowanie obiektowe ścieżki Rust w Coddy. Lekcja 45 z 61.

Gdy używasz typów generycznych z ograniczeniami cech, takimi jak <T: Summary>, Rust określa dokładny typ w czasie kompilacji. Nazywa się to statycznym wywoływaniem: kompilator generuje wyspecjalizowany kod dla każdego konkretnego typu, którego używasz. Jest to szybkie, ale ma pewne ograniczenie: zmienna może przechowywać tylko jeden konkretny typ.

Co zrobić, jeśli potrzebujesz jednej zmiennej, która może przechowywać w czasie działania programu różne typy? Właśnie tu przydają się obiekty cech. Używając Box<dyn Trait>, możesz przechowywać dowolny typ implementujący tę cechę:

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!"
}

Słowo kluczowe dyn wskazuje na dynamiczne wywoływanie metod: Rust wyszukuje metodę do wywołania w czasie działania programu, a nie podczas kompilacji. Box jest niezbędny, ponieważ obiekty cech nie mają znanego rozmiaru; Box zapewnia wskaźnik o stałym rozmiarze.

Można to ująć tak: typy generyczne mówią „Działam z typem T”, a obiekty cech mówią „Działam z dowolnym typem, który potrafi to zrobić”. Ceną za elastyczność obsługi różnych typów za pomocą tej samej zmiennej jest niewielki koszt w czasie działania programu.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system wypożyczania pojazdów, który pokazuje możliwości dynamicznego wywoływania metod! Utworzysz cechę definiującą, co może robić każdy pojazd przeznaczony do wypożyczenia, a następnie użyjesz Box<dyn Trait>, aby przechowywać różne typy pojazdów w tej samej zmiennej.

Podzielisz swój kod na dwa pliki:

  • vehicle.rs: Zdefiniuj publiczną cechę Rentable z metodą rental_info, która przyjmuje &self i zwraca String. Następnie utwórz dwie publiczne struktury:
    • Car: z publicznym polem model (String). Metoda rental_info powinna zwracać Car: {model}
    • Bike: z publicznym polem brand (String). Metoda rental_info powinna zwracać Bike: {brand}
  • main.rs: Zaimportuj moduł pojazdu i użyj danych wejściowych, aby zademonstrować dynamiczne wywoływanie metod. Utwórz zmienną Box<dyn Rentable>, która najpierw przechowuje Car, wypisz informacje o wypożyczeniu, a następnie przypisz do tej samej zmiennej Bike i ponownie wypisz informacje o wypożyczeniu.

Cała sztuczka polega na tym, że pojedyncza zmienna typu Box<dyn Rentable> może przechowywać Car lub Bike: Rust ustala w czasie wykonywania, którą metodę rental_info wywołać. Tak właśnie działa dynamiczne wywoływanie metod!

Wynik powinien zawierać informacje o obu pojazdach:

Car: {model}
Bike: {brand}

Na przykład dla danych wejściowych Tesla Model 3 i Trek:

Car: Tesla Model 3
Bike: Trek

Otrzymasz dwa dane wejściowe: model samochodu i markę roweru.

Spróbuj swoich sił

mod vehicle;

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

fn main() {
    // Wczytaj dane wejściowe
    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: Utwórz zmienną typu Box<dyn Rentable>, która przechowuje Car
    // Wyświetl informacje o wynajmie za pomocą metody rental_info()
    
    // TODO: Przypisz do tej samej zmiennej Bike
    // Ponownie wyświetl informacje o wynajmie
    
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Rust online