🚀 BAŞTAN SONA RUST DİLİ BÖLÜM-15: Smart Pointers (Akıllı İşaretçiler) 🧠

Konu Özeti

Rust'ta akıllı işaretçiler (smart pointers) ele alınıyor. Box ile heap tahsisi, Deref ve Drop trait'leri, çoklu sahiplik için Rc ve içsel değiştirilebilirlik için RefCell yapıları örneklerle açıklanıyor. Özyinelemeli tipler, referans sayımı ve çalışma zamanı ödünç denetimi gibi konulara değiniliyor.

🚀 BAŞTAN SONA RUST DİLİ BÖLÜM-15: Smart Pointers (Akıllı İşaretçiler) 🧠

Selamlar! Serimizin 15. bölümüne hoş geldiniz.

Önceki bölümde Rust'ın işlevsel programlama tarafını oluşturan Closures ve Iterators konularını görmüştük. Bu bölümde ise Rust'ta bellek yönetimini bir üst seviyeye taşıyan, özel yeteneklere sahip pointer yapılarını yani Smart Pointers (Akıllı İşaretçiler) konusunu inceleyeceğiz.

Smart Pointer'lar genellikle bellek adresini tutan basit bir referanstan (`&`) farklı olarak, metadata ve ek işlevsellik barındıran Struct yapılarıdır. Çoğu zaman tuttukları verinin sahipliğine (ownership) sahiptirler.

Smart Pointer'ları anlamamızı sağlayan iki temel Trait vardır:
• Deref Trait'i: Smart Pointer'ın normal bir referans gibi davranmasını (`*` operatörü ile veriye erişilmesini) sağlar.
• Drop Trait'i: Smart Pointer scope dışına çıktığında çalışacak temizleme (destructor) kodunu tanımlamamıza izin verir.

---

📌 1. `Box<T>`: Heap Üzerinde Veri Tahsisi 📦

`Box<T>`, veriyi Stack yerine Heap bellekte saklamamızı sağlayan en basit Smart Pointer'dır. Stack üzerinde sadece Heap'i gösteren bir pointer tutulur.

Ne Zaman `Box<T>` Kullanılır?
1. Derleme zamanında boyutu bilinmeyen bir tipi, kesin boyut gerektiren bir yerde kullanırken (Örn: Özyinelemeli/Recursive tipler).
2. Büyük miktarda veriyi kopyalamadan (Move maliyetini düşürerek) sahipliğini devretmek istediğimizde.
3. Bir değerin somut tipi yerine belirli bir Trait'i uyguladığından emin olmak istediğimizde (`Trait Objects` - `Box<dyn Trait>`).

fn main() {
// 5 değerini Heap bellek üzerinde saklıyoruz
let b = Box::new(5);
println!("b = {}", b);
} // b scope dışına çıktığında hem Stack'teki pointer hem Heap'teki veri silinir.

Özyinelemeli (Recursive) Tipler ve `Box<T>`:
Rust derleyicisinin derleme anında her tipin kapladığı alanı kesin olarak bilmesi gerekir. Kendini içeren bir yapı (Örn: Lisp'teki Cons List veya LinkedList) direkt yazılırsa derleyici sonsuz boyut hatası verir.

// BU KOD DERLENMEZ!
// enum List {
// Cons(i32, List), // HATA: Sonsuz boyut!
// Nil,
// }

// Box<T> kullanarak sabit pointer boyutu sağlarız:
enum List {
Cons(i32, Box<List>),
Nil,
}

use List::{Cons, Nil};

fn main() {
let liste = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}

---

🔗 2. `Deref` Trait'i İle Dereferencing 🎯

`Deref` Trait'ini uygulamak, Smart Pointer'a bir referans gibi davranabilme yeteneği kazandırır. Bu sayede `*` (dereference) operatörünü kendi yazdığımız yapılar üzerinde kullanabiliriz.

use std::eek:ps::Deref;

struct BenimBox<T>(T);

impl<T> BenimBox<T> {
fn new(x: T) -> BenimBox<T> {
BenimBox(x)
}
}

impl<T> Deref for BenimBox<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

fn main() {
let x = 5;
let y = BenimBox::new(x);

assert_eq!(5, x);
assert_eq!(5, *y); // Deref Trait'i sayesinde *y arkada *(y.deref()) komutuna dönüştürülür!
}

---

🧹 3. `Drop` Trait'i İle Otomatik Temizlik 🧼

`Drop` Trait'i, bir değer scope dışına çıktığında özelleştirilmiş temizleme kodunun otomatik olarak yürütülmesini sağlar. Dosya kapatma, ağ bağlantısı kesme veya bellek boşaltma gibi işlemler burada yapılır.

struct CikisBildirici {
veri: String,
}

impl Drop for CikisBildirici {
fn drop(&mut self) {
println!("'{}' verisini tutan CikisBildirici bellekten temizleniyor!", self.veri);
}
}

fn main() {
let a = CikisBildirici { veri: String::from("A Nesnesi") };
let b = CikisBildirici { veri: String::from("B Nesnesi") };
println!("CikisBildiriciler oluşturuldu.");
}
// Scope biterken tersten sırayla B ve A için drop() fonksiyonu otomatik çalışır!

---

👥 4. `Rc<T>`: Reference Counted (Çoklu Sahiplik) 🔄

Bölüm 4'te öğrendiğimiz Ownership kuralına göre bir verinin sadece TEK BİR sahibi olabilir. Ancak bazı durumlarda (grafik yapıları, birden fazla düğümün aynı veriyi göstermesi) bir verinin birden fazla sahibi olması gerekir.

`Rc<T>` (Reference Counted), veriye olan referansların sayısını tutar.
• Referans sayısı 0 olduğunda veri güvenle bellekten silinir.
• Yalnızca TEK THREAD'li (Single-threaded) senaryolarda çalışır!
• Veriyi varsayılan olarak Immutable (Değiştirilemez) sunar.

use std::rc::Rc;

enum List {
Cons(i32, Rc<List>),
Nil,
}

use List::{Cons, Nil};

fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("a'nın referans sayısı: {}", Rc::strong_count(&a)); // 1

let b = Cons(3, Rc::clone(&a)); // Rc::clone veriyi kopyalamaz, sayacı 1 artırır!
println!("b oluşturulduktan sonra a'nın referans sayısı: {}", Rc::strong_count(&a)); // 2

{
let c = Cons(4, Rc::clone(&a));
println!("c oluşturulduktan sonra a'nın referans sayısı: {}", Rc::strong_count(&a)); // 3
} // c scope dışına çıkar, sayaç 1 azalır

println!("c scope dışına çıktıktan sonra a'nın referans sayısı: {}", Rc::strong_count(&a)); // 2
}

---

🛠️ 5. `RefCell<T>` ve Interior Mutability Deseni 🔑

Normalde Rust kuralları gereği immutable bir verinin içini değiştiremezsiniz. Ancak Interior Mutability (İçsel Değiştirilebilirlik) deseni, veri dışarıdan immutable görünse dahi içindeki veriyi değiştirmenize imkan tanır.

`RefCell<T>`, Borrowing (Ödünç Alma) kurallarını derleme zamanı yerine Çalışma Zamanında (Runtime) denetler!

• Normal Referanslar (& / &mut): Borrow Kontrolü Compile-Time'da yapılır. Kural ihlali derleme hatası verir.
• `RefCell<T>`: Borrow Kontrolü Runtime'da yapılır. Kural ihlali runtime panic verir.

use std::cell::RefCell;

fn main() {
let veri = RefCell::new(42);

{
// .borrow_mut() ile çalışma zamanında mutable referans alırız
let mut mut_ref = veri.borrow_mut();
*mut_ref += 10;
} // mut_ref scope dışına çıktı, mutable ödünç alma bitti.

// .borrow() ile immutable referans alırız
println!("Yeni Değer: {}", *veri.borrow()); // 52
}

`Rc<T>` ve `RefCell<T>` Birlikte Kullanımı (Çoklu + Mutable Sahiplik):
Aynı veriye hem birden fazla sahibin erişmesi hem de verinin değiştirilebilmesi istendiğinde `Rc<RefCell<T>>` kombinasyonu kullanılır.

use std::rc::Rc;
use std::cell::RefCell;

fn main() {
let ortak_deger = Rc::new(RefCell::new(5));

let a = Rc::clone(&ortak_deger);
let b = Rc::clone(&ortak_deger);

*ortak_deger.borrow_mut() += 10; // Ortak değer güncellendi!

println!("a üzerinden okunan değer: {}", a.borrow()); // 15
println!("b üzerinden okunan değer: {}", b.borrow()); // 15
}

---

📝 Bölüm Özeti ve Gelecek Bölüm 🔮

Bu bölümde `Box<T>`, `Deref` ve `Drop` Trait'lerini, çoklu sahiplik sağlayan `Rc<T>` ve çalışma zamanı borrow denetimi yapan `RefCell<T>` Smart Pointer yapılarını derinlemesine inceledik.

👉 Bölüm 16'da Bizi Ne Bekliyor?
• Eşzamanlılık (Concurrency) ve Paralel Programlama ⚡
• Threads (İş Parçacıkları): `thread::spawn` ve `join`

Thread'ler Arası Veri Taşıma: Channels (`mpsc`)
• Paylaşılan Bellek Yönetimi: `Arc<T>` ve `Mutex<T>`

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

 
Yanıt yazmak için giriş yapmalısınız
Forum özelliklerini kullanmak ve Level 2 üyelik satın almak için hesabınıza giriş yapın.
135,050Konular
3,297,703Mesajlar
326,233Kullanıcılar
membiSon Üye
Üst Alt