-
Sadrazamv4
ig:sadrazam_v4 tg: Sadrazam.spy
🚀 BAŞTAN SONA RUST DİLİ BÖLÜM-13: Lifetimes (Yaşam Süreleri) ve Referans Güvenliği ⏳
Selamlar! Serimizin 13. bölümüne hoş geldiniz.
Önceki bölümde Rust'ın polimorfizm ve arayüz altyapısını oluşturan Trait yapısını görmüştük. Bu bölümde ise Rust'ı diğer tüm dillerden ayıran, derleme zamanında (compile-time) dangling pointer (sallanan referans) hatalarını imkansız kılan **Lifetimes (Yaşam Süreleri)** konusunu inceleyeceğiz.
Lifetimes, Rust derleyicisinin içindeki **Borrow Checker (Ödünç Kontrolcüsü)** mekanizmasının referansların ne kadar süre geçerli kaldığını doğrulamak için kullandığı parametrelerdir.
---
📌 1. Borrow Checker ve Dangling Reference Önleme 🛡️
Rust'ın temel amacı, bellekten silinmiş bir veriyi gösteren geçersiz referansların (dangling references) oluşmasını engellemektir.
Aşağıdaki geçersiz koda bakalım:
{
let r; ///////// +-- 'a (r'nin yaşam süresi)
// |
{ // |
let x = 5; // -+-- 'b (x'in yaşam süresi)
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // <------ HATA! 'x' burada silindi ama 'r' onu gösteriyor!
} ///////// +
Borrow Checker burada `'b` yaşam süresinin (x), `'a` yaşam süresinden (r) daha kısa olduğunu görür. `r`, silinmiş bir belleği göstereceği için derleyici kodu **reddeder**.
---
🔍 2. Neden Lifetime Belirteçlerine (`'a`) İhtiyaç Duyarız? ❓
Derleyici çoğu zaman yaşam sürelerini kendisi otomatik olarak çıkarır (Lifetime Elision). Ancak bir fonksiyon birden fazla referans alıp geriye referans döndürdüğünde, dönen referansın HANGİ girdi referansına ait olduğunu kestiremez.
Aşağıdaki koda bakalım:
// BU KOD DERLENMEZ!
/*
fn en_uzun(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
*/
Derleyici şu soruyu sorar: "Dönen `&str` referansı `x`'in yaşam süresine mi bağlı, yoksa `y`'nin yaşam süresine mi?" Bunu bilmeden dönen referansın güvenliğini doğrulayamaz.
---
✍️ 3. Lifetime Syntax ve Belirteç Kullanımı 🏷️
Lifetime parametreleri kesme işareti (`'`) ile başlar ve genellikle küçük harflerle yazılır (`'a`, `'b`, `'c`).
Jenerikler gibi fonksiyon isminden sonra `<'a>` şeklinde bildirilir.
fn en_uzun<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
#### Bu İmzayı Nasıl Okumalıyız?
`en_uzun` fonksiyonu `'a` adında bir yaşam süresi parametresi alır.
1. `x` parametresi en az `'a` kadar yaşar.
2. `y` parametresi en az `'a` kadar yaşar.
3. **Dönen referans da en az `'a` kadar yaşar.**
Buradaki `'a`, hem `x` hem de `y`'nin yaşam sürelerinin kesişimi (en küçük/kısa olanı) kadar geçerlidir.
fn main() {
let string1 = String::from("Uzun metin");
let result;
{
let string2 = String::from("Kısa");
result = en_uzun(string1.as_str(), string2.as_str());
println!("En uzun metin: {}", result); // Geçerli! Hem string1 hem string2 hayatta.
}
// println!("{}", result); // HATA! string2'nin yaşam süresi bittiği için result kullanılamaz.
}
---
🏗️ 4. Struct Yapılarında Lifetime Kullanımı 🧱
Bir Struct, içerisinde **referans tutuyorsa**, o referansın yaşam süresini Struct tanımında açıkça belirtmek ZORUNDADIR. Bu, Struct örneğinin, içinde tuttuğu referanstan daha uzun yaşayamayacağını garanti eder.
struct ÖnemliBolum<'a> {
bolum: &'a str, // Struct, 'bolum' referansı yaşadığı sürece geçerlidir
}
fn main() {
let roman = String::from("Beni İsmail diye çağırın. Yıllar önce...");
let ilk_cumle = roman.split('.').next().expect("Cümle bulunamadı");
let duyuru = ÖnemliBolum {
bolum: ilk_cumle,
};
println!("Önemli kısım: {}", duyuru.bolum);
}
---
🙈 5. Lifetime Elision (Yaşam Süresi İhmal Kuralları) 🙈
Her referans kullandığımızda `'a` yazmak zorunda kalsaydık Rust yazmak işkenceye dönüşürdü. Rust derleyicisi, yaygın 3 kuralı uygulayarak manuel Lifetime yazımını otomatik tamamlar:
1. **Girdi Kuralı:** Referans olan her parametreye ayrı bir lifetime atanır (`fn foo<'a, 'b>(x: &'a i32, y: &'b i32)`).
2. **Çıktı Kuralı 1:** Eğer tek bir girdi lifetime parametresi varsa, o lifetime tüm çıktı parametrelerine atanır (`fn foo<'a>(x: &'a i32) -> &'a i32`).
3. **Çıktı Kuralı 2 (Metot Kuralı):** Eğer girdilerden biri `&self` veya `&mut self` ise, `self`'in yaşam süresi tüm çıktı parametrelerine atanır.
Eğer bu 3 kural uygulandıktan sonra dönen referansın yaşam süresi belirsiz kalıyorsa, derleyici sizden manuel olarak yazmanızı ister.
---
♾️ 6. Statik Yaşam Süresi: `'static` 🌍
`'static`, programın çalıştığı **tüm süre boyunca** bellekte yaşayan özel bir lifetime'dır.
Tüm metin sabitleri (string literals) varsayılan olarak `'static` yaşam süresine sahiptir, çünkü doğrudan binary executable dosyasının içine gömülürler:
let s: &'static str = "Ben ikili dosyaya gömülüyüm ve hiç silinmem!";
> **Uyarı:** Derleyici hatalarını çözmek için hemen her yere `'static` eklemek anti-pattern'dir. Gerçekten program sonuna kadar yaşaması gerekmeyen verilere `'static` verilmemelidir.
---
📝 Bölüm Özeti ve Gelecek Bölüm 🔮
Bu bölümde Borrow Checker'ın çalışma ilkelerini, Lifetimes (`'a`) kavramını, fonksiyon ve Struct'larda referans güvenliğini, Elision kurallarını ve `'static` terimini öğrendik.
👉 **Bölüm 14'te Bizi Ne Bekliyor?**
• İşlevsel Programlama (Functional Rust): **Closures (Anonim Fonksiyonlar)**
• Closure Çeşitleri (`Fn`, `FnMut`, `FnOnce`) ve Ortamdan Değişken Yakalama (`move`)
• **Iterators (Yineleyiciler)** ve Iterator Tüketici/Adaptör Metotları (`map`, `filter`, `collect`)
Sorularınızı konu altından iletebilirsiniz. Bir sonraki bölümde görü
şmek üzere! 🦀✨
Selamlar! Serimizin 13. bölümüne hoş geldiniz.
Önceki bölümde Rust'ın polimorfizm ve arayüz altyapısını oluşturan Trait yapısını görmüştük. Bu bölümde ise Rust'ı diğer tüm dillerden ayıran, derleme zamanında (compile-time) dangling pointer (sallanan referans) hatalarını imkansız kılan **Lifetimes (Yaşam Süreleri)** konusunu inceleyeceğiz.
Lifetimes, Rust derleyicisinin içindeki **Borrow Checker (Ödünç Kontrolcüsü)** mekanizmasının referansların ne kadar süre geçerli kaldığını doğrulamak için kullandığı parametrelerdir.
---
📌 1. Borrow Checker ve Dangling Reference Önleme 🛡️
Rust'ın temel amacı, bellekten silinmiş bir veriyi gösteren geçersiz referansların (dangling references) oluşmasını engellemektir.
Aşağıdaki geçersiz koda bakalım:
{
let r; ///////// +-- 'a (r'nin yaşam süresi)
// |
{ // |
let x = 5; // -+-- 'b (x'in yaşam süresi)
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // <------ HATA! 'x' burada silindi ama 'r' onu gösteriyor!
} ///////// +
Borrow Checker burada `'b` yaşam süresinin (x), `'a` yaşam süresinden (r) daha kısa olduğunu görür. `r`, silinmiş bir belleği göstereceği için derleyici kodu **reddeder**.
---
🔍 2. Neden Lifetime Belirteçlerine (`'a`) İhtiyaç Duyarız? ❓
Derleyici çoğu zaman yaşam sürelerini kendisi otomatik olarak çıkarır (Lifetime Elision). Ancak bir fonksiyon birden fazla referans alıp geriye referans döndürdüğünde, dönen referansın HANGİ girdi referansına ait olduğunu kestiremez.
Aşağıdaki koda bakalım:
// BU KOD DERLENMEZ!
/*
fn en_uzun(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
*/
Derleyici şu soruyu sorar: "Dönen `&str` referansı `x`'in yaşam süresine mi bağlı, yoksa `y`'nin yaşam süresine mi?" Bunu bilmeden dönen referansın güvenliğini doğrulayamaz.
---
✍️ 3. Lifetime Syntax ve Belirteç Kullanımı 🏷️
Lifetime parametreleri kesme işareti (`'`) ile başlar ve genellikle küçük harflerle yazılır (`'a`, `'b`, `'c`).
Jenerikler gibi fonksiyon isminden sonra `<'a>` şeklinde bildirilir.
fn en_uzun<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
#### Bu İmzayı Nasıl Okumalıyız?
`en_uzun` fonksiyonu `'a` adında bir yaşam süresi parametresi alır.
1. `x` parametresi en az `'a` kadar yaşar.
2. `y` parametresi en az `'a` kadar yaşar.
3. **Dönen referans da en az `'a` kadar yaşar.**
Buradaki `'a`, hem `x` hem de `y`'nin yaşam sürelerinin kesişimi (en küçük/kısa olanı) kadar geçerlidir.
fn main() {
let string1 = String::from("Uzun metin");
let result;
{
let string2 = String::from("Kısa");
result = en_uzun(string1.as_str(), string2.as_str());
println!("En uzun metin: {}", result); // Geçerli! Hem string1 hem string2 hayatta.
}
// println!("{}", result); // HATA! string2'nin yaşam süresi bittiği için result kullanılamaz.
}
---
🏗️ 4. Struct Yapılarında Lifetime Kullanımı 🧱
Bir Struct, içerisinde **referans tutuyorsa**, o referansın yaşam süresini Struct tanımında açıkça belirtmek ZORUNDADIR. Bu, Struct örneğinin, içinde tuttuğu referanstan daha uzun yaşayamayacağını garanti eder.
struct ÖnemliBolum<'a> {
bolum: &'a str, // Struct, 'bolum' referansı yaşadığı sürece geçerlidir
}
fn main() {
let roman = String::from("Beni İsmail diye çağırın. Yıllar önce...");
let ilk_cumle = roman.split('.').next().expect("Cümle bulunamadı");
let duyuru = ÖnemliBolum {
bolum: ilk_cumle,
};
println!("Önemli kısım: {}", duyuru.bolum);
}
---
🙈 5. Lifetime Elision (Yaşam Süresi İhmal Kuralları) 🙈
Her referans kullandığımızda `'a` yazmak zorunda kalsaydık Rust yazmak işkenceye dönüşürdü. Rust derleyicisi, yaygın 3 kuralı uygulayarak manuel Lifetime yazımını otomatik tamamlar:
1. **Girdi Kuralı:** Referans olan her parametreye ayrı bir lifetime atanır (`fn foo<'a, 'b>(x: &'a i32, y: &'b i32)`).
2. **Çıktı Kuralı 1:** Eğer tek bir girdi lifetime parametresi varsa, o lifetime tüm çıktı parametrelerine atanır (`fn foo<'a>(x: &'a i32) -> &'a i32`).
3. **Çıktı Kuralı 2 (Metot Kuralı):** Eğer girdilerden biri `&self` veya `&mut self` ise, `self`'in yaşam süresi tüm çıktı parametrelerine atanır.
Eğer bu 3 kural uygulandıktan sonra dönen referansın yaşam süresi belirsiz kalıyorsa, derleyici sizden manuel olarak yazmanızı ister.
---
♾️ 6. Statik Yaşam Süresi: `'static` 🌍
`'static`, programın çalıştığı **tüm süre boyunca** bellekte yaşayan özel bir lifetime'dır.
Tüm metin sabitleri (string literals) varsayılan olarak `'static` yaşam süresine sahiptir, çünkü doğrudan binary executable dosyasının içine gömülürler:
let s: &'static str = "Ben ikili dosyaya gömülüyüm ve hiç silinmem!";
> **Uyarı:** Derleyici hatalarını çözmek için hemen her yere `'static` eklemek anti-pattern'dir. Gerçekten program sonuna kadar yaşaması gerekmeyen verilere `'static` verilmemelidir.
---
📝 Bölüm Özeti ve Gelecek Bölüm 🔮
Bu bölümde Borrow Checker'ın çalışma ilkelerini, Lifetimes (`'a`) kavramını, fonksiyon ve Struct'larda referans güvenliğini, Elision kurallarını ve `'static` terimini öğrendik.
👉 **Bölüm 14'te Bizi Ne Bekliyor?**
• İşlevsel Programlama (Functional Rust): **Closures (Anonim Fonksiyonlar)**
• Closure Çeşitleri (`Fn`, `FnMut`, `FnOnce`) ve Ortamdan Değişken Yakalama (`move`)
• **Iterators (Yineleyiciler)** ve Iterator Tüketici/Adaptör Metotları (`map`, `filter`, `collect`)
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