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.
Wyzwanie
ŁatwyZbudujmy 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ęRentablez metodąrental_info, która przyjmuje&selfi zwracaString. Następnie utwórz dwie publiczne struktury:Car: z publicznym polemmodel(String). Metodarental_infopowinna zwracaćCar: {model}Bike: z publicznym polembrand(String). Metodarental_infopowinna 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 przechowujeCar, wypisz informacje o wypożyczeniu, a następnie przypisz do tej samej zmiennejBikei 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: TrekOtrzymasz 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
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Metody i zachowanie
Wprowadzenie do bloków implementacjiParametr selfMetody modyfikująceFunkcje powiązaneWiele bloków implementacjiŁańcuchowe wywoływanie metodPowtórzenie — działania na prostokącie4Projekt: Wirtualne zwierzątko
Definiowanie zwierzątkaKarmienie zwierzątka7Standardowe cechy
Cecha DebugCecha DisplayClone i CopyCechy równościPowtórka – punkt do wydrukowania10Projekt: System dokumentów
Trait DrawKomponent tekstowy2Hermetyzacja i moduły
Podstawy modułówSłowo kluczowe publicPola prywatneGetterySetteryPodsumowanie — bezpieczna skrytka5Typy generyczne
Struktury generyczneMetody generyczneWiele typów generycznychFunkcje generycznePodsumowanie – punkt współrzędnych8Cechy jako ograniczenia
Składnia ograniczeń cechWiele ograniczeńKlauzula whereZwracanie typów z użyciem cechPodsumowanie — generyczna drukarka11Wzorce projektowe w Rust
Wzorzec NewtypeKompozycjaTrait DropFrom i IntoPodsumowanie — atrapowy inteligentny wskaźnik3Zaawansowane typy wyliczeniowe
Typy wyliczeniowe z danymiMetody typów wyliczeniowychDopasowywanie wariantów danychTyp wyliczeniowy Option — powtórzeniePodsumowanie — typ wyliczeniowy Shape6Definiowanie cech
Czym jest cecha?Implementowanie cechImplementacje domyślneNadpisywanie wartości domyślnychCechy z parametramiPowtórzenie — odtwarzacz multimedialny9Obiekty cech
Dynamiczne wywołanieWektory obiektów cechIterowanie po obiektach cechPodsumowanie – zarządzanie zooPoćwicz samodzielnie: Kompilator Rust online