-
Sadrazamv4
ig:sadrazam_v4 tg: Sadrazam.spy
🚀 BAŞTAN SONA RUST DİLİ BÖLÜM-11: Generics (Jenerikler) ve Kod Esnekliği 🧬
Selamlar! Serimizin 11. bölümüne hoş geldiniz.
Önceki bölümde projemizi modüllere nasıl ayıracağımızı, `pub` ile erişim kontrollerini ve `use` kullanımını öğrenmiştik. Bu bölümde ise kod tekrarını önleyen, türden bağımsız esnek ve güvenli yapılar oluşturmamızı sağlayan **Generics (Jenerikler)** konusunu inceleyeceğiz.
Generics, türleri (types) soyutlayarak tek bir fonksiyon, struct veya enum tanımı ile farklı veri tipleriyle çalışmamıza imkan tanır.
---
📌 1. Neden Generics Kullanırız? 🤔
Diyelim ki bir dizideki en büyük elemanı bulan bir fonksiyon yazmak istiyoruz.
Önce `i32` için yazalım:
fn en_buyuk_i32(liste: &[i32]) -> &i32 {
let mut en_buyuk = &liste[0];
for eleman in liste {
if eleman > en_buyuk {
en_buyuk = eleman;
}
}
en_buyuk
}
Peki aynı işlemi `char` veya `f64` tipleri için de yapmamız gerekirse ne olacak? Fonksiyonu kopyalayıp sadece veri tiplerini değiştirmek ciddi bir kod tekrarına (code duplication) yol açar.
İşte tam bu noktada **Generics** devreye girer!
---
⚙️ 2. Fonksiyonlarda Generics Kullanımı 🛠️
Jenerik tür parametreleri genellikle tek harfli büyük harflerle temsil edilir (Standart olarak `T` kullanılır - "Type" anlamında).
Açısal parantezler `<T>` kullanılarak fonksiyona tanımlanır:
// T tipinde bir dilim (slice) alır ve T tipinde referans döner
fn en_buyuk<T: std::cmp:
artialOrd>(liste: &[T]) -> &T {
let mut en_buyuk = &liste[0];
for eleman in liste {
if eleman > en_buyuk { // Karşılaştırma yapabilmek için PartialOrd kısıtlaması şarttır
en_buyuk = eleman;
}
}
en_buyuk
}
fn main() {
let sayi_listesi = vec![34, 50, 25, 100, 65];
let sonuc = en_buyuk(&sayi_listesi);
println!("En büyük sayı: {}", sonuc);
let karakter_listesi = vec!['y', 'm', 'a', 'q'];
let sonuc = en_buyuk(&karakter_listesi);
println!("En büyük karakter: {}", sonuc);
}
> **Not:** `T: std::cmp:
artialOrd` ifadesi bir **Trait Bound**'dur. Derleyiciye "Gelecek `T` tipi birbiriyle karşılaştırılabilir (`>`) bir tip olmalıdır" mesajı verir. Trait konusunu Bölüm 12'de detaylıca işleyeceğiz!
---
🏗️ 3. Struct Yapılarında Generics 🧱
Struct tanımlarken de bir veya birden fazla jenerik tip parametresi kullanabiliriz.
### Tek Tip Parametreli Struct:
Tüm jenerik alanlar aynı tipten olmak zorundadır.
struct Nokta<T> {
x: T,
y: T,
}
fn main() {
let tamsayi = Nokta { x: 5, y: 10 }; // T -> i32
let ondalik = Nokta { x: 1.0, y: 4.0 }; // T -> f64
// let hatali = Nokta { x: 5, y: 4.0 }; // HATA! x i32 iken y f64 olamaz.
}
### Birden Fazla Tip Parametreli Struct:
Farklı tipleri desteklemek istiyorsak birden fazla parametre tanımlarız (`<T, U>`):
struct NoktaEsnek<T, U> {
x: T,
y: U,
}
fn main() {
let karma = NoktaEsnek { x: 5, y: 4.0 }; // T -> i32, U -> f64. Sorunsuz çalışır!
}
---
🎨 4. Enum Yapılarında Generics 🎭
Rust'ın standart kütüphanesindeki en temel yapılar Generics üzerine kurulmuştur. Daha önce gördüğümüz `Option<T>` ve `Result<T, E>` buna en güzel örnektir:
// Tek jenerik parametreli Enum
enum Option<T> {
Some(T),
None,
}
// İki jenerik parametreli Enum
enum Result<T, E> {
Ok(T),
Err(E),
}
Bu sayede `Option`, bir tam sayı (`Option<i32>`), metin (`Option<String>`) veya kendi yazdığımız bir Struct (`Option<Kullanici>`) tutabilir.
---
⚙️ 5. `impl` Bloklarında Generics ve Metot Tanımları 🛠️
Jenerik bir Struct için metot yazarken `impl` kelimesinden hemen sonra `<T>` parametresini bildirmemiz gerekir.
struct Nokta<T> {
x: T,
y: T,
}
// Jenerik metot bloğu
impl<T> Nokta<T> {
fn x(&self) -> &T {
&self.x
}
}
// Sadece belirli bir tip için geçerli olan metot bloğu (Concrete implementation)
impl Nokta<f32> {
fn orijine_uzaklik(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p1 = Nokta { x: 5, y: 10 };
println!("p1.x = {}", p1.x());
let p2 = Nokta { x: 3.0_f32, y: 4.0_f32 };
println!("Orijine uzaklık: {}", p2.orijine_uzaklik());
// p1.orijine_uzaklik(); // HATA! p1 (i32) olduğu için bu metoda sahip değildir.
}
---
⚡ 6. Generics Performans Etkisi: Monomorphization 🚀
C++ veya Java gibi dillerdeki jenerik yapılarda "çalışma zamanı maliyeti" (runtime overhead) endişesi yaşanabilir.
Rust'ta ise **Generics kullanmanın ÇALIŞMA ZAMANI MALİYETİ SIFIRDIR (Zero-Cost Abstraction)!**
Derleyici, derleme aşamasında (compile-time) jenerik kodları inceler ve kullanılan somut tiplere göre kodu çoğaltır. Bu işleme **Monomorphization** denir.
Örnek olarak, siz şunu yazarsınız:
let int_option = Some(5);
let float_option = Some(5.0);
Derleyici bunu şu şekilde iki ayrı somut tipe dönüştürür:
enum Option_i32 {
Some(i32),
None,
}
enum Option_f64 {
Some(f64),
None,
}
Sonuç olarak: Çalışma zamanında hiçbir arama/tip kontrolü yapılmaz, doğrudan somut tiple çalışıldığı için kod C hızında çalışır!
---
📝 Bölüm Özeti ve Gelecek Bölüm 🔮
Bu bölümde Generics kavramını, fonksiyon, struct ve enum'larda esnek tür yönetimini, özel `impl` bloklarını ve Rust'ın Monomorphization mekanizmasını gördük.
👉 **Bölüm 12'de Bizi Ne Bekliyor?**
• Rust'ın Arayüz/İnterfase Yapısı: **Traits (Nitelikler)**
• Trait tanımlama ve bir Struct'a `impl Trait
for Struct` ile uygulama
• **Trait Bounds** (`where` cümlesi)
• `impl Trait` dönüş tipleri
Sorularınızı konu altından iletebilirsiniz. Bir sonraki bölümde görüşmek üzere! 🦀✨
Selamlar! Serimizin 11. bölümüne hoş geldiniz.
Önceki bölümde projemizi modüllere nasıl ayıracağımızı, `pub` ile erişim kontrollerini ve `use` kullanımını öğrenmiştik. Bu bölümde ise kod tekrarını önleyen, türden bağımsız esnek ve güvenli yapılar oluşturmamızı sağlayan **Generics (Jenerikler)** konusunu inceleyeceğiz.
Generics, türleri (types) soyutlayarak tek bir fonksiyon, struct veya enum tanımı ile farklı veri tipleriyle çalışmamıza imkan tanır.
---
📌 1. Neden Generics Kullanırız? 🤔
Diyelim ki bir dizideki en büyük elemanı bulan bir fonksiyon yazmak istiyoruz.
Önce `i32` için yazalım:
fn en_buyuk_i32(liste: &[i32]) -> &i32 {
let mut en_buyuk = &liste[0];
for eleman in liste {
if eleman > en_buyuk {
en_buyuk = eleman;
}
}
en_buyuk
}
Peki aynı işlemi `char` veya `f64` tipleri için de yapmamız gerekirse ne olacak? Fonksiyonu kopyalayıp sadece veri tiplerini değiştirmek ciddi bir kod tekrarına (code duplication) yol açar.
İşte tam bu noktada **Generics** devreye girer!
---
⚙️ 2. Fonksiyonlarda Generics Kullanımı 🛠️
Jenerik tür parametreleri genellikle tek harfli büyük harflerle temsil edilir (Standart olarak `T` kullanılır - "Type" anlamında).
Açısal parantezler `<T>` kullanılarak fonksiyona tanımlanır:
// T tipinde bir dilim (slice) alır ve T tipinde referans döner
fn en_buyuk<T: std::cmp:
let mut en_buyuk = &liste[0];
for eleman in liste {
if eleman > en_buyuk { // Karşılaştırma yapabilmek için PartialOrd kısıtlaması şarttır
en_buyuk = eleman;
}
}
en_buyuk
}
fn main() {
let sayi_listesi = vec![34, 50, 25, 100, 65];
let sonuc = en_buyuk(&sayi_listesi);
println!("En büyük sayı: {}", sonuc);
let karakter_listesi = vec!['y', 'm', 'a', 'q'];
let sonuc = en_buyuk(&karakter_listesi);
println!("En büyük karakter: {}", sonuc);
}
> **Not:** `T: std::cmp:
---
🏗️ 3. Struct Yapılarında Generics 🧱
Struct tanımlarken de bir veya birden fazla jenerik tip parametresi kullanabiliriz.
### Tek Tip Parametreli Struct:
Tüm jenerik alanlar aynı tipten olmak zorundadır.
struct Nokta<T> {
x: T,
y: T,
}
fn main() {
let tamsayi = Nokta { x: 5, y: 10 }; // T -> i32
let ondalik = Nokta { x: 1.0, y: 4.0 }; // T -> f64
// let hatali = Nokta { x: 5, y: 4.0 }; // HATA! x i32 iken y f64 olamaz.
}
### Birden Fazla Tip Parametreli Struct:
Farklı tipleri desteklemek istiyorsak birden fazla parametre tanımlarız (`<T, U>`):
struct NoktaEsnek<T, U> {
x: T,
y: U,
}
fn main() {
let karma = NoktaEsnek { x: 5, y: 4.0 }; // T -> i32, U -> f64. Sorunsuz çalışır!
}
---
🎨 4. Enum Yapılarında Generics 🎭
Rust'ın standart kütüphanesindeki en temel yapılar Generics üzerine kurulmuştur. Daha önce gördüğümüz `Option<T>` ve `Result<T, E>` buna en güzel örnektir:
// Tek jenerik parametreli Enum
enum Option<T> {
Some(T),
None,
}
// İki jenerik parametreli Enum
enum Result<T, E> {
Ok(T),
Err(E),
}
Bu sayede `Option`, bir tam sayı (`Option<i32>`), metin (`Option<String>`) veya kendi yazdığımız bir Struct (`Option<Kullanici>`) tutabilir.
---
⚙️ 5. `impl` Bloklarında Generics ve Metot Tanımları 🛠️
Jenerik bir Struct için metot yazarken `impl` kelimesinden hemen sonra `<T>` parametresini bildirmemiz gerekir.
struct Nokta<T> {
x: T,
y: T,
}
// Jenerik metot bloğu
impl<T> Nokta<T> {
fn x(&self) -> &T {
&self.x
}
}
// Sadece belirli bir tip için geçerli olan metot bloğu (Concrete implementation)
impl Nokta<f32> {
fn orijine_uzaklik(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p1 = Nokta { x: 5, y: 10 };
println!("p1.x = {}", p1.x());
let p2 = Nokta { x: 3.0_f32, y: 4.0_f32 };
println!("Orijine uzaklık: {}", p2.orijine_uzaklik());
// p1.orijine_uzaklik(); // HATA! p1 (i32) olduğu için bu metoda sahip değildir.
}
---
⚡ 6. Generics Performans Etkisi: Monomorphization 🚀
C++ veya Java gibi dillerdeki jenerik yapılarda "çalışma zamanı maliyeti" (runtime overhead) endişesi yaşanabilir.
Rust'ta ise **Generics kullanmanın ÇALIŞMA ZAMANI MALİYETİ SIFIRDIR (Zero-Cost Abstraction)!**
Derleyici, derleme aşamasında (compile-time) jenerik kodları inceler ve kullanılan somut tiplere göre kodu çoğaltır. Bu işleme **Monomorphization** denir.
Örnek olarak, siz şunu yazarsınız:
let int_option = Some(5);
let float_option = Some(5.0);
Derleyici bunu şu şekilde iki ayrı somut tipe dönüştürür:
enum Option_i32 {
Some(i32),
None,
}
enum Option_f64 {
Some(f64),
None,
}
Sonuç olarak: Çalışma zamanında hiçbir arama/tip kontrolü yapılmaz, doğrudan somut tiple çalışıldığı için kod C hızında çalışır!
---
📝 Bölüm Özeti ve Gelecek Bölüm 🔮
Bu bölümde Generics kavramını, fonksiyon, struct ve enum'larda esnek tür yönetimini, özel `impl` bloklarını ve Rust'ın Monomorphization mekanizmasını gördük.
👉 **Bölüm 12'de Bizi Ne Bekliyor?**
• Rust'ın Arayüz/İnterfase Yapısı: **Traits (Nitelikler)**
• Trait tanımlama ve bir Struct'a `impl Trait
for Struct` ile uygulama
• **Trait Bounds** (`where` cümlesi)
• `impl Trait` dönüş tipleri
Sorularınızı konu altından iletebilirsiniz. Bir sonraki bölümde görüşmek üzere! 🦀✨
🔒 Bu içeriği görmek için giriş yapın