Wywołanie statyczne a dynamiczne
Kompromisy
Wywołanie statyczne a dynamiczne to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Dwa sposoby wywoływania
Gdy wywołujesz metodę traitu, Rust musi ustalić, która konkretna implementacja zostanie uruchomiona. Istnieją dwie strategie: statyczne wywołanie (ustalane w czasie kompilacji) oraz dynamiczne wywołanie (ustalane w czasie działania).
Statyczne wywołanie z typami generycznymi
Funkcje generyczne używają statycznego wywołania. Kompilator generuje wyspecjalizowaną kopię dla każdego konkretnego typu — proces ten nazywa się monomorfizacją.
trait Area { fn area(&self) -> f64; }
struct Square { s: f64 }
impl Area for Square { fn area(&self) -> f64 { self.s * self.s } }
fn print_area<T: Area>(shape: &T) {
println!("{}", shape.area());
}
fn main() {
print_area(&Square { s: 4.0 });
}Monomorfizacja
Dla każdego typu, z którym wywołujesz funkcję, kompilator tworzy jej dedykowaną wersję. Wywołanie metody staje się bezpośrednim wywołaniem bez wyszukiwania, więc jest równie szybkie jak kod napisany ręcznie.
Dynamiczne wywołanie z dyn
Obiekty traitów używają dynamicznego wywołania. Jedna funkcja obsługuje wszystkie typy, a adres metody jest znajdowany w czasie działania za pomocą vtable.
trait Area { fn area(&self) -> f64; }
struct Square { s: f64 }
impl Area for Square { fn area(&self) -> f64 { self.s * self.s } }
fn print_area(shape: &dyn Area) {
println!("{}", shape.area());
}
fn main() {
print_area(&Square { s: 4.0 });
}Vtable
Obiekt traitu jest grubym wskaźnikiem: jedna jego część wskazuje dane, a druga — vtable zawierającą adresy metod. Każde wywołanie odwołuje się do odpowiedniej pozycji w tej tabeli.
Kompromis dotyczący szybkości
Statyczne wywołanie jest szybsze przy każdym wywołaniu i może być rozwijane przez kompilator, ale generuje więcej kodu maszynowego. Dynamiczne wywołanie dodaje niewielkie pośrednie odwołanie, lecz utrzymuje mały rozmiar kodu.
W większości aplikacji różnica jest pomijalna — wybór zależy od potrzebnej elastyczności.
Kompromis dotyczący rozmiaru kodu
Wywoływanie funkcji generycznej z wieloma typami tworzy wiele kopii, co może powiększać plik binarny. Pojedyncza funkcja dyn pozwala uniknąć tego powielania.
Dlatego biblioteki czasami wewnętrznie preferują obiekty traitów.
Kompromis dotyczący elastyczności
Typy generyczne wymuszają jeden konkretny typ w każdym miejscu wywołania, więc nie mogą przechowywać mieszanych typów w kolekcji. Obiekty traitów mogą to robić. Jeśli potrzebujesz heterogenicznej listy, rozwiązaniem jest dynamiczne wywołanie.
trait Area { fn area(&self) -> f64; }
struct Square { s: f64 }
struct Rect { w: f64, h: f64 }
impl Area for Square { fn area(&self) -> f64 { self.s * self.s } }
impl Area for Rect { fn area(&self) -> f64 { self.w * self.h } }
fn main() {
let shapes: Vec<Box<dyn Area>> = vec![Box::new(Square { s: 2.0 }), Box::new(Rect { w: 3.0, h: 4.0 })];
let total: f64 = shapes.iter().map(|s| s.area()).sum();
println!("{}", total);
}Łączenie obu podejść
Można je łączyć: funkcja generyczna może przyjmować na granicy impl Trait, a wewnętrznie przechowywać wartości jako Box<dyn Trait>. Każdego podejścia używaj tam, gdzie najlepiej pasuje.
Wskazówki dotyczące wyboru
Szybkie zasady:
- Jeden typ na wywołanie, ścieżka krytyczna pod względem wydajności? Użyj typów generycznych (statycznie).
- Mieszane typy w kolekcji? Użyj
dyn(dynamicznie). - Obawiasz się rozmiaru pliku binarnego? Skłaniaj się ku
dyn.
Oba podejścia nie generują kosztów tam, gdzie ma to znaczenie
Rust nigdy nie dodaje narzutu, o który nie prosisz. Typy generyczne nie generują kosztu w czasie działania, a obiekty traitów kosztują tylko jedno pośrednie odwołanie za pomocą wskaźnika. To Ty jawnie wybierasz kompromis.
Szybki sprawdzian
Sprawdź swoją wiedzę o sposobach wywoływania.
Podsumowanie
Porównano dwa style wywoływania:
- Statyczne (typy generyczne): monomorfizowane, szybkie, większy plik binarny, jeden typ na wywołanie
- Dynamiczne (
dyn): wyszukiwanie w vtable, elastyczne, mniejszy kod, umożliwia mieszane kolekcje - Wybieraj na podstawie potrzeb dotyczących elastyczności, wydajności i rozmiaru pliku binarnego
Często zadawane pytania
Czy lekcja „Wywołanie statyczne a dynamiczne” jest bezpłatna?
Tak — pełny tekst „Wywołanie statyczne a dynamiczne” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Co nauczysz się w „Wywołanie statyczne a dynamiczne”?
Kompromisy Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?
Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Wywołanie statyczne a dynamiczne”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?
Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Definiowanie cech
- Obiekty cech i dyn
- Wywołanie statyczne a dynamiczne
- Metody domyślne