Genel türler (generics), belirli bir türe bağımsız kod yazmanıza olanak tanır. Açık köşeli parantezler ile uygulanır:
fn max<T: PartialOrd>(a: T, b: T) -> T { if a > b { a } else { b } }
Burada T, PartialOrd trait'i ile sınırlı olan genel bir türdür.
Generic parametreler <T> ile ilan edilir, ancak trait bounds ile sınırlanabilir; örneğin, <T: Display>. Bu, derleyiciye yalnızca gerekli trait'i gerçekleştiren türlerin kullanılabileceğini belirtmek için bir yoldur.
Rust'ta generics için iki tür dispatch şekli vardır:
dyn Trait kullanılıyorsa, sanal tablo (vtable) aracılığıyla çağrı yapılır.Makine koduna etkisi: Trait bounds ile olan generic kullanımı (dyn Trait olmadan) monomorfizasyona yol açar: ikili dosyanın büyümesine, ancak maksimum hızda sonuçlanır. dyn Trait kullanımı, ikili dosyayı küçültür, ancak performansta düşüş vardır.
Soru: Aşağıdaki fonksiyon var mı?
fn do_something<T: Debug>(value: &T)
Derleyici, her tür için ayrı bir do_something fonksiyonu oluşturacak mı yoksa evrensel bir uygulama mı kullanacak?
Tipik yanlış cevap: Trait bound sayesinde tüm türler için tek bir fonksiyon kullanacaktır.
Doğru cevap: Derleyici, her tür için ayrı bir kopyasını (monomorfizasyon) oluşturur, çünkü trait bound generic fonksiyonu vtable aracılığıyla "evrensel" hale getirmez. Evrensellik yalnızca dyn Trait (dinamik dispatch) ile ortaya çıkar.
Örnek:
fn print_val<T: std::fmt::Debug>(val: T) { println!("{:?}", val); } // Farklı türlerle her çağrı için kendi versiyonu oluşturulacaktır.
Hikaye
Büyük genel nesneler içeren bir projede, ikili dosyanın beklenenden çok daha büyük hale geldiği keşfedildi. Sonradan anlaşıldı ki sebep, sınırlama olmadan genel fonksiyonların geniş çapta kullanılmasıydı. Onlarca türle yapılan çağrılar, yürütülebilir dosyanın boyutunun üssel büyümesine (code bloat) neden oldu ve bu durum yalnızca CI'deki sürüm derlemesinde ortaya çıktı.
Hikaye
Geliştiricilerden biri, bir trait bind ile genel bir parametre alıyordu, bu nedenle bu kodun "dinamik" dispatch ile çalıştığını düşündü. Bu durum, sunucudaki bellek tüketiminde aşırıya ve sürekli artan kod ve işlemci tarafından önbelleğe alınması sebebiyle performans kaybına yol açtı.
Hikaye
Bir kütüphanede, genel bir trait'i
Selftipi ile (örneğin, trait Clone)dyn Traitolarak kullanmaya çalıştılar, bu Rust'ta desteklenmiyor ve derleme hatasına neden oldu. Arayüz açıkça yeniden yazılmalıydı, aksi takdirde genel API dinamik modda çalışmazdı ve arayüz compile-time düzeyinde değiştirilmeliydi.