Dinamik Gönderim
Coddy'nin Rust Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 45 / 61.
<T: Summary> gibi trait sınırlarına sahip jenerikler kullandığınızda, Rust tam türü derleme zamanında belirler. Buna statik dağıtım denir; derleyici, kullandığınız her somut tür için özelleştirilmiş kod oluşturur. Hızlıdır, ancak bir sınırlama vardır: bir değişken yalnızca tek bir belirli türü tutabilir.
Çalışma zamanında farklı türleri tutabilen tek bir değişkene ihtiyacınız olursa ne olur? Trait nesneleri burada devreye girer. Box<dyn Trait> kullanarak, trait'i uygulayan herhangi bir türü saklayabilirsiniz:
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 anahtar kelimesi dinamik gönderimi (dynamic dispatch) belirtir—Rust, hangi metodun çağrılacağını derleme zamanı yerine çalışma zamanında belirler. Box gereklidir çünkü trait nesnelerinin bilinen bir boyutu yoktur; Box sabit boyutlu bir işaretçi sağlar.
Şöyle düşünün: generic'ler "T türü ile çalışırım" derken, trait nesneleri "Bunu yapabilen herhangi bir şeyle çalışırım" der. Buradaki takas, aynı değişken üzerinden farklı türleri yönetme esnekliği karşılığında ödenen küçük bir çalışma zamanı (runtime) maliyetidir.
Görev
KolayDinamik sevkiyatın (dynamic dispatch) gücünü gösteren bir araç kiralama sistemi inşa edelim! Herhangi bir kiralanabilir aracın neler yapabileceğini tanımlayan bir trait oluşturacak, ardından farklı araç türlerini aynı değişkende saklamak için Box<dyn Trait> kullanacaksınız.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
vehicle.rs:&selfalan ve birStringdöndürenrental_infometoduna sahip, halka açık (public) birRentabletrait'i tanımlayın. Ardından iki adet halka açık struct oluşturun:Car— halka açık birmodelalanına (String) sahip.rental_infometodu"Car: {model}"döndürmelidir.Bike— halka açık birbrandalanına (String) sahip.rental_infometodu"Bike: {brand}"döndürmelidir.
main.rs: Araç modülünüzü dahil edin ve dinamik sevkiyatı göstermek için girdileri kullanın. Önce birCartutanBox<dyn Rentable>türünde bir değişken oluşturun, kiralama bilgisini yazdırın, ardından aynı değişkeni birBiketutacak şekilde yeniden atayın ve kiralama bilgisini tekrar yazdırın.
Buradaki sihir, Box<dyn Rentable> türündeki tek bir değişkenin hem Car hem de Bike tutabilmesidir; Rust, çalışma zamanında (runtime) hangi rental_info metodunun çağrılacağını anlar. İşte bu, dinamik sevkiyattır!
Çıktınız her iki aracın bilgisini de göstermelidir:
Car: {model}
Bike: {brand}Örneğin, Tesla Model 3 ve Trek girdileriyle:
Car: Tesla Model 3
Bike: Trekİki girdi alacaksınız: araba modeli ve bisiklet markası.
Kopya kağıdı
Rust, generic yapılarla statik gönderim (static dispatch) kullanır; derleyici, derleme zamanında her tür için özelleştirilmiş kod üretir. Bu hızlıdır ancak bir değişkenin yalnızca belirli bir türü tutabileceği anlamına gelir.
Trait nesneleri, dinamik gönderimi (dynamic dispatch) etkinleştirerek tek bir değişkenin çalışma zamanında farklı türleri tutmasına olanak tanır. Trait'i uygulayan herhangi bir türü saklamak için Box<dyn Trait> kullanın:
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 anahtar kelimesi dinamik gönderimi belirtir; metot çağrıları çalışma zamanında çözümlenir. Trait nesnelerinin derleme zamanında bilinen bir boyutu olmadığı için Box gereklidir.
Temel fark: Generic yapılar "T türü ile çalışırım" (belirli bir tür) derken, trait nesneleri "bu trait'i uygulayan her şeyle çalışırım" (aynı değişken üzerinden birden fazla tür) der. Buradaki ödünleşim, esneklik karşılığında küçük bir çalışma zamanı maliyetidir.
Kendin dene
mod vehicle;
use vehicle::{Rentable, Car, Bike};
fn main() {
// Girdiyi oku
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: Bir Car tutan bir Box<dyn Rentable> değişkeni oluşturun
// rental_info() metodunu kullanarak kiralama bilgisini yazdırın
// TODO: Aynı değişkeni bir Bike tutacak şekilde yeniden atayın
// Kiralama bilgisini tekrar yazdırın
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Object Oriented Programming bölümündeki tüm dersler
1Metotlar ve Davranış
Uygulama Bloklarına GirişSelf ParametresiDeğiştirilebilir Metotlarİlişkili FonksiyonlarBirden Fazla Uygulama BloğuMetot ZincirlemeÖzet - Dikdörtgen Eylemleri4Proje: Sanal Evcil Hayvan
Evcil Hayvanı TanımlamaEvcil Hayvanı Besleme2Kapsülleme ve Modüller
Modül TemelleriPublic Anahtar KelimesiPrivate AlanlarGetter'larSetter'larÖzet - Güvenli Kasa5Generic Yapılar
Generic Struct'larGeneric MetotlarÇoklu Generic TürlerGeneric FonksiyonlarÖzet - Koordinat Noktası8Sınırlandırıcı Olarak Trait'ler
Trait Sınırlandırıcı SözdizimiÇoklu SınırlandırıcılarWhere CümleciğiTrait'ler ile Tip DöndürmeÖzet - Generic Yazıcı11Rust'ta Tasarım Kalıpları
Newtype KalıbıKompozisyonDrop Trait'iFrom ve IntoÖzet - Smart Pointer Mock3İleri Seviye Enum'lar
Veri İçeren Enum'larEnum MetotlarıVeri Varyantlarını EşlemeOption Enum'ına Yeniden BakışÖzet - Shape Enum6Trait Tanımlama
Trait Nedir?Traitleri UygulamaVarsayılan UygulamalarVarsayılanları Geçersiz KılmaParametreli TraitlerÖzet - Media Player9Trait Nesneleri
Dinamik GönderimTrait VektörleriTrait Nesneleri Üzerinde YinelemeÖzet - Hayvanat Bahçesi Yöneticisi