Rust'ta trait'ler bir arayüz tanımlama yoludur: belirli bir tür tarafından uygulanması gereken metodlar kümesini tanımlarlar. Bir trait'in uygulanması, generikler kullanarak yöntemlerin dinamik olarak geçersiz kılınmasını sağlar.
Statik dispatch (generikler ve bound’lar aracılığıyla):
Belirli bir metod uygulaması derleme zamanında çağrılır. Genel kod kullanılır (impl<T: Trait> veya fn foo<T: Trait>(t: T)), derleme sırasında her tür için ayrı bir fonksiyon versiyonu oluşturulur (monomorfizasyon).
Dinamik dispatch:
Fonksiyon bir trait'e referans olarak nesneleri kabul ettiğinde (&dyn Trait), derleyici hangi türün kullanılacağını önceden bilmez. Metodun çağrılması, çalışma zamanında vtable (sanallaştırılmış tablo) aracılığıyla gerçekleşir.
Örnek:
trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } // Statik dispatch def print_animal_static<T: Animal>(a: T) { a.speak(); } // Dinamik dispatch def print_animal_dyn(a: &dyn Animal) { a.speak(); }
Ne zaman kullanılacağı:
Trait-tipi ile generik değerleri Vec<Trait> içinde saklamak mümkün mü? Neden?
Cevap:
Hayır, bu şekilde yapmak mümkün değil. Vec boyutlandırılmış türler gerektirir ve trait'ler kendileri bir boyuta sahip değildir. Box<dyn Trait> veya trait'lere referanslar kullanmalısınız.
// let v: Vec<Trait> = vec![]; // derleme hatası let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // geçerli
Hikaye
Box veya Rc kullanmadan trait nesnelerinden oluşan bir koleksiyon oluşturma denemesi:
// let coll: Vec<MyTrait> = vec![]; // hata: ... türü için değerlerin boyutu bilinemiyor
Hikaye
Hikaye
Demo müşterisinde, farklı protokollere etkileşimde bulunmak için geçici nesnelere referanslar (&dyn Trait) saklamaya çalıştılar, bu da dangling references ve runtime panic'lere neden oldu. Açık sahiplik için Box<dyn Trait>'e geçmek zorunda kaldık.