Generic Struct'lar
Coddy'nin Rust Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 24 / 61.
Şu ana kadar oluşturduğunuz her struct, belirli ve somut türlere sahip alanlara sahipti. Bir Pet, String türünde bir isme; bir Rectangle ise f64 türünde boyutlara sahiptir. Peki ya herhangi bir veri türünü tutabilen bir struct oluşturmak isterseniz?
İşte burada generics devreye girer. Generics, birden fazla türle çalışan esnek ve yeniden kullanılabilir kodlar yazmanıza olanak tanır. Belirli bir türü sabit olarak kodlamak yerine, struct kullandığınızda somut bir türle değiştirilen bir yer tutucu —genellikle T— kullanırsınız.
İşte herhangi bir türü barındırabilen basit bir sarmalayıcı yapı (struct):
struct Wrapper<T> {
value: T,
}
Struct isminden sonra gelen <T>, genel bir tür parametresi (generic type parameter) tanımlar. Struct içinde T, fiilen kullanacağınız herhangi bir türün yerini tutan bir temsilci görevi görür. Bir örnek (instance) oluşturduğunuzda, Rust somut türü (concrete type) kendisi çıkarır:
let int_wrapper = Wrapper { value: 42 }; // T, i32'dir
let float_wrapper = Wrapper { value: 3.14 }; // T, f64'dür
let text_wrapper = Wrapper { value: "hello" }; // T, &str'dir
Bunların her biri farklı bir somut türdür—Wrapper<i32>, Wrapper<f64> ve Wrapper<&str>—ancak hepsi aynı struct tanımını paylaşır. Bu, temelde aynı şeyi yapan ayrı IntWrapper, FloatWrapper ve StringWrapper struct'ları yazma ihtiyacını ortadan kaldırır.
T harfi sadece bir gelenektir ("Type" kelimesinin kısaltmasıdır). Herhangi bir geçerli tanımlayıcı kullanabilirsiniz, ancak T, Rust'ta ve jenerik içeren diğer çoğu dilde standarttır.
Görev
KolayHerhangi bir türde değer tutabilen genel (generic) bir konteyner oluşturalım! Generics (genel türler) özelliğinin kodunuzu nasıl esnek ve yeniden kullanılabilir hale getirdiğini gösteren bir Container yapısı (struct) oluşturacaksınız.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
container.rs:Ttüründe tek bir genel alanı (field)itemolan,Container<T>adında genel bir yapı tanımlayın. Bu konteyner herhangi bir türü (tam sayılar, ondalıklı sayılar, dizeler veya başka herhangi bir şey) tutabilmelidir.main.rs: Konteyner modülünüzü dahil edin ve farklı veri türlerini tutan konteynerler oluşturarak esnekliğini gösterin. Üç adet konteyner oluşturacak ve her birinin ne tuttuğunu görüntüleyeceksiniz.
Ana dosyanızda aşağıdaki konteynerleri oluşturun:
- Bir tam sayı değeri tutan konteyner (
i32olarak ayrıştırılan ilk girdi) - Bir ondalıklı sayı değeri tutan konteyner (
f64olarak ayrıştırılan ikinci girdi) - Bir dize tutan konteyner (
Stringolarak tutulan üçüncü girdi)
Çıktınız, her bir konteynerin içeriğini şu formatta görüntülemelidir:
Integer container: {value}
Float container: {value}
String container: {value}Örneğin, 42, 3.14 ve hello girdileriyle:
Integer container: 42
Float container: 3.14
String container: helloAynı Container<T> tanımının üç farklı tür için de nasıl çalıştığına dikkat edin; işte generics'in gücü budur!
Üç girdi alacaksınız: bir tam sayı değeri, bir ondalıklı sayı değeri ve bir dize değeri.
Kopya kağıdı
Generikler, bir tür parametresi yer tutucusu kullanarak her türlü veriyi tutabilen struct'lar oluşturmanıza olanak tanır.
Generik bir struct bildirmek için, bir tür parametresi (geleneksel olarak T) ile birlikte açılı ayraçlar kullanın:
struct Wrapper<T> {
value: T,
}
<T> generik bir tür parametresi bildirir ve struct içindeki T, gerçek tür için bir yer tutucu görevi görür.
Örnekler oluştururken, Rust somut türü çıkarımlar:
let int_wrapper = Wrapper { value: 42 }; // T, i32'dir
let float_wrapper = Wrapper { value: 3.14 }; // T, f64'dür
let text_wrapper = Wrapper { value: "hello" }; // T, &str'dir
Her bir örnek, aynı struct tanımını paylaşırken farklı bir somut tür (Wrapper<i32>, Wrapper<f64>, Wrapper<&str>) haline gelir.
Kendin dene
mod container;
use container::Container;
fn main() {
// Üç girişi oku
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let int_value: i32 = input1.trim().parse().expect("Failed to parse integer");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let float_value: f64 = input2.trim().parse().expect("Failed to parse float");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let string_value = input3.trim().to_string();
// TODO: Container<T> kullanarak üç konteyner oluşturun
// 1. int_value tutan bir tam sayı (integer) konteyneri
// 2. float_value tutan bir ondalıklı sayı (float) konteyneri
// 3. string_value tutan bir metin (string) konteyneri
// TODO: Her bir konteynerin içeriğini istenen formatta yazdırın
// Tam sayı konteyneri: {value}
// Ondalıklı sayı konteyneri: {value}
// Metin konteyneri: {value}
}
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 Player