Trait Vektörleri
Coddy'nin Rust Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 46 / 61.
Box<dyn Trait>'in tek bir değişkenin farklı türleri tutmasına nasıl izin verdiğini gördünüz. Asıl güç, bunu koleksiyonlarla birleştirdiğinizde —tek bir vektörde birden fazla farklı türü depolayarak— ortaya çıkar.
Normal bir vektör, tüm öğelerin aynı türde olmasını gerektirir. Aynı zamanda kedileri de içeren bir Vec<Dog> yapısına sahip olamazsınız. Ancak trait nesneleri ile, paylaşılan bir trait'i uygulayan herhangi bir şeyi tutan bir vektör oluşturabilirsiniz:
trait Speak {
fn speak(&self) -> String;
}
struct Dog;
struct Cat;
struct Bird;
impl Speak for Dog {
fn speak(&self) -> String { String::from("Woof!") }
}
impl Speak for Cat {
fn speak(&self) -> String { String::from("Meow!") }
}
impl Speak for Bird {
fn speak(&self) -> String { String::from("Tweet!") }
}
fn main() {
let animals: Vec<Box<dyn Speak>> = vec![
Box::new(Dog),
Box::new(Cat),
Box::new(Bird),
];
}
Vec<Box<dyn Speak>> türü, "kutulanmış trait nesnelerinden oluşan bir vektör" anlamına gelir. Her bir öğe, Speak trait'ini uygulayan bir şeye işaret eden bir Box'tır; gerçek türler farklılık gösterebilir. Her bir değeri vektöre eklerken Box::new() ile sarmalarsınız.
Bu desen; UI bileşenleri, oyun varlıkları veya eklenti sistemleri gibi ilişkili ancak farklı nesnelerden oluşan koleksiyonları yönetmeniz gereken esnek sistemler oluşturmak için temeldir.
Görev
KolayFarklı türdeki uyarıları işleyebilen bir bildirim sistemi kuralım! Trait nesnelerini kullanarak e-postalar, SMS mesajları ve anlık bildirimler gibi çeşitli bildirim türlerini tek bir vektörde saklayan bir koleksiyon oluşturacaksınız.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
notifications.rs:&selfalan ve gönderilen bildirimi açıklayan birStringdöndürensendmetoduna sahip genel (public) birNotifytrait'i tanımlayın. Ardından üç adet genel struct oluşturun:Email— genel birrecipientalanına (String) sahip.sendmetodu"Email to: {}"formatında bir değer döndürmelidir.Sms— genel birphonealanına (String) sahip.sendmetodu"SMS to: {}"formatında bir değer döndürmelidir.Push— genel birdevicealanına (String) sahip.sendmetodu"Push to: {}"formatında bir değer döndürmelidir.
main.rs: Bildirim modülünüzü dahil edin ve her üç bildirim türünü de tutanVec<Box<dyn Notify>>tipinde bir vektör oluşturun. Her bir bildirim türünden birer tane oluşturmak için sağlanan girdileri kullanın, bunları sırasıyla (Email, Sms, Push) vektörünüze ekleyin ve ardından vektörün ilk elemanı üzerindesend()metodunu çağırmanın sonucunu yazdırın.
Buradaki temel kavram, Email, Sms ve Push tamamen farklı struct'lar olmalarına rağmen, Notify trait'ini paylaştıkları için hepsinin aynı vektörde bir arada bulunabilmesidir. Her eleman, trait nesnesini oluşturmak için Box::new() ile sarmalanır.
Çıktınız ilk bildirimi göstermelidir:
Email to: {recipient}Örneğin, "alice@example.com", "555-1234" ve "iPhone-12" girdileriyle:
Email to: alice@example.comÜç girdi alacaksınız: e-posta alıcısı, telefon numarası ve cihaz adı.
Kopya kağıdı
Bir Vec<Box<dyn Trait>>, hepsi aynı trait'i uyguladığı sürece tek bir vektörde birden fazla farklı türün saklanmasına olanak tanır.
Normal vektörler tüm öğelerin aynı türde olmasını gerektirir, ancak trait nesneleri heterojen koleksiyonlara olanak sağlar:
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 animals: Vec<Box<dyn Speak>> = vec![
Box::new(Dog),
Box::new(Cat),
];
}
Vektöre ekleme yaparken her öğe Box::new() ile sarmalanır. Paylaşılan trait'i uyguladıkları sürece gerçek türler farklılık gösterebilir.
Kendin dene
mod notifications;
use notifications::{Notify, Email, Sms, Push};
fn main() {
// Girdileri oku
let mut recipient = String::new();
std::io::stdin().read_line(&mut recipient).expect("Failed to read line");
let recipient = recipient.trim().to_string();
let mut phone = String::new();
std::io::stdin().read_line(&mut phone).expect("Failed to read line");
let phone = phone.trim().to_string();
let mut device = String::new();
std::io::stdin().read_line(&mut device).expect("Failed to read line");
let device = device.trim().to_string();
// TODO: Farklı bildirim türlerini tutmak için bir Vec<Box<dyn Notify>> oluşturun
// TODO: Girdileri kullanarak Email, Sms ve Push örnekleri oluşturun
// TODO: Bunları Box::new() kullanarak sırasıyla (Email, Sms, Push) vektöre ekleyin
// TODO: İlk eleman üzerinde send() metodunu çağırmanın sonucunu 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