Menu
Coddy logo textTech

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.

challenge icon

Görev

Kolay

Dinamik 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: &self alan ve bir String döndüren rental_info metoduna sahip, halka açık (public) bir Rentable trait'i tanımlayın. Ardından iki adet halka açık struct oluşturun:
    • Car — halka açık bir model alanına (String) sahip. rental_info metodu "Car: {model}" döndürmelidir.
    • Bike — halka açık bir brand alanına (String) sahip. rental_info metodu "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 bir Car tutan Box<dyn Rentable> türünde bir değişken oluşturun, kiralama bilgisini yazdırın, ardından aynı değişkeni bir Bike tutacak ş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
    
}
quiz iconKendini test et

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