Rust Ownership ve Borrowing: Her Seyi Aciklayan Kapsamli Rehber

Rust ownership ve borrowing kavramlarini pratik orneklerle ogrenin. Move semantikleri, referanslar, lifetime'lar ve borrow checker ile guvenli, performansli Rust kodu yazmayi kesfedin.

Rust ownership and borrowing memory management visualization

Rust'ta ownership ve borrowing, dilin bellek guvenligini saglayan temel mekanizmalari olusturur. Garbage collector kullanan dillerden farkli olarak Rust, derleme zamaninda borrow checker araciligiyla kati kurallar uygulayarak null pointer hatalari, veri yarislari ve use-after-free gibi hata siniflarini calisma zamani maliyeti olmadan tamamen ortadan kaldirir.

Ownership'in Uc Temel Kurali

Rust'ta her degerin tam olarak bir sahibi (owner) vardir. Sahip kapsam disindan ciktiginda deger bellekten silinir (drop). Sahiplik aktarilabilir (move) veya gecici olarak odunc verilebilir (borrow). Bu uc kural, garbage collection mekanizmasinin yerini tamamen alir.

Move Semantikleri Garbage Collection'in Yerini Nasil Alir

Cogu programlama dili, birden fazla degiskenin heap'te ayrilmis ayni veriye isaret etmesine izin verir. Rust farkli bir yaklasim benimser: heap uzerindeki bir degeri baska bir degiskene atamak, degeri tasir (move) ve orijinal baglamayi gecersiz kilar. Derleyici bunu sifir maliyetle zorunlu kilar.

move_semantics.rsrust
fn main() {
    let original = String::from("interview prep");
    let moved = original; // ownership transfers here

    // println!("{}", original); // compile error: value moved
    println!("{}", moved); // works fine
}

Bu yaklasim double-free hatalarini onler. String tipi heap uzerinde bellek ayirimi yapar, bu nedenle Rust herhangi bir anda yalnizca bir degiskenin bu ayirima sahip olmasini garanti eder. i32 veya bool gibi yalnizca stack uzerinde yasayan tipler Copy trait'ini implemente eder ve tasima yerine kopyalanir.

copy_vs_move.rsrust
fn main() {
    let x: i32 = 42;
    let y = x; // copy, not move -- i32 is Copy
    println!("x = {}, y = {}", x, y); // both valid

    let s1 = String::from("hello");
    let s2 = s1.clone(); // explicit deep copy
    println!("s1 = {}, s2 = {}", s1, s2); // both valid after clone
}

Copy ve Clone arasindaki fark mulakatlarda onemli bir konudur: Copy ortuk ve ucuzdur (bit duzeyi kopyalama), Clone ise acik ve maliyetli olabilir (heap ayirimi gerektirebilir).

Degismez Referanslarla Borrowing

Sahipligi her yere aktarmak kodu kullanilamaz hale getirir. Rust borrowing mekanizmasi, sahipligi aktarmadan bir degere erisim odunc vererek bu sorunu cozer. Degismez bir referans (&T) yalnizca okuma erisimi saglar ve birden fazla degismez referans ayni anda bir arada var olabilir.

immutable_borrowing.rsrust
fn calculate_length(s: &String) -> usize {
    s.len() // read access through the reference
} // s goes out of scope, but doesn't drop the String (not the owner)

fn main() {
    let greeting = String::from("hello, Rust");
    let len = calculate_length(&greeting); // borrow, don't move
    println!("'{}' has {} characters", greeting, len); // greeting still valid
}

& sembol bir referans olusturarak degeri odunc alir. Fonksiyon imzasindaki &String, calculate_length fonksiyonunun sahiplik almadan odunc aldigini bildirir. Fonksiyon donduktaen sonra cagrici tam sahipligini korur.

Borrowing Kurallari Ozeti

Herhangi bir anda, bir deger ya birden fazla degismez referansa (&T) ya da tam olarak bir degistirilebilir referansa (&mut T) sahip olabilir. Asla her ikisi ayni anda olamaz. Bu kural, derleme zamaninda veri yarislarini onler.

Degistirilebilir Referanslar ve Mustakillik Kurali

Degistirilebilir referanslar (&mut T) yazma erisimi saglar ancak mustakillik zorunlulugu getirir: belirli bir kapsamda bir degere yalnizca bir degistirilebilir referans bulunabilir. Bu, iki farkli kod parcasinin ayni veriyi esanli olarak degistirmesini engeller.

mutable_borrowing.rsrust
fn append_greeting(s: &mut String) {
    s.push_str(", welcome to Rust!"); // modify through mutable ref
}

fn main() {
    let mut message = String::from("Hello");
    append_greeting(&mut message);
    println!("{}", message); // "Hello, welcome to Rust!"
}

mut anahtar sozcugu uc yerde gorulur: degisken baglamada (let mut), referans tipinde (&mut) ve fonksiyon parametresinde. Her ucu de zorunludur. Ayni kapsamda ikinci bir degistirilebilir referans olusturmaya calismak derleme hatasina neden olur.

exclusivity_rule.rsrust
fn main() {
    let mut data = String::from("shared state");

    let r1 = &mut data;
    // let r2 = &mut data; // compile error: second mutable borrow
    println!("{}", r1);

    // After r1's last usage, a new mutable borrow is allowed
    let r3 = &mut data; // this works -- non-lexical lifetimes
    r3.push_str(" updated");
    println!("{}", r3);
}

Rust 2021 surumu leksik olmayan yasamsureleri (NLL - Non-Lexical Lifetimes) kullanir: bir odunc alma, kapsam blogunun sonunda degil, son kullanim noktasinda sona erer. Bu, guvenligi feda etmeden mustakillik kuralini daha ergonomik hale getirir.

Rust mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Lifetime'lar: Derleyiciye Referanslarin Ne Kadar Yasadigini Soyleme

Lifetime'lar, Rust'in referanslarin isaret ettigi veriden daha uzun yasamasini engellemesinin yoludur. Cogu durumda derleyici, lifetime elision kurallari araciligiyla lifetime'lari otomatik olarak cikarir. Birden fazla referansin etkilesime girdigi durumlarda acik annotasyonlar gerekli hale gelir.

lifetime_annotation.rsrust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let result;
    let string1 = String::from("Rust ownership");
    {
        let string2 = String::from("borrowing");
        result = longest(string1.as_str(), string2.as_str());
        println!("Longest: {}", result);
    }
}

'a sozdizimi bir lifetime parametresidir -- yeni bir kavram degil, halihazirda var olan iliskileri isaretler. Fonksiyon imzasi su anlama gelir: "cikis referansi, herhangi bir giris referansindan daha uzun yasamaz." Derleyici bunu sarkan referanslari (dangling references) onlemek icin kullanir.

Struct'larda Borrowing ve Lifetime Sinirlari

Referans tutan struct'lar lifetime parametreleri bildirmek zorundadir. Bu, struct'in referans verdigi veriden daha uzun yasamasini engeller -- C/C++'da sarkan isaretcilerin (dangling pointer) yaygin bir kaynagi olan bu sorun Rust'ta tamamen ortadan kaldirilir.

struct_lifetime.rsrust
struct Excerpt<'a> {
    text: &'a str,
}

impl<'a> Excerpt<'a> {
    fn summary(&self) -> &str {
        let end = self.text.len().min(20);
        &self.text[..end]
    }
}

fn main() {
    let article = String::from("Rust ownership model eliminates memory bugs");
    let excerpt = Excerpt {
        text: article.as_str(),
    };
    println!("Summary: {}", excerpt.summary());
}

Excerpt<'a> icindeki 'a lifetime'i, struct'in gecerliligini temel alindigi string'e baglar. article degiskenini excerpt'tan once bellekten silmek derleme hatasina yol acar.

Mulakatlarda Sik Yapilan Hata

Sarkan referans (dangling reference) sorulari Rust mulakatlarinda sikca karsimiza cikar. Cevap her zaman aynidir: Rust, derleme zamaninda lifetime analizi ile bunlari onler. Calisma zamani kontrolu yoktur, null isaretci yoktur.

Gercek Dunyada Ownership Kaliplari

Uretim ortamindaki Rust kodu birkaç tekrarlanan ownership kalibina dayanir. Bu kaliplari tanimak hem gelistirme hizini hem de mulakat performansini arttirir.

ownership_patterns.rsrust
// Pattern 1: Take ownership, return a new value
fn process_and_return(mut input: String) -> String {
    input.push_str(" -- processed");
    input
}

// Pattern 2: Borrow for read-only inspection
fn contains_keyword(text: &str, keyword: &str) -> bool {
    text.to_lowercase().contains(&keyword.to_lowercase())
}

// Pattern 3: Borrow mutably for in-place modification
fn sanitize(input: &mut String) {
    *input = input.trim().to_string();
}

fn main() {
    let raw = String::from("user input");
    let processed = process_and_return(raw);

    let found = contains_keyword(&processed, "input");
    println!("Contains 'input': {}", found);

    let mut padded = String::from("  spaces everywhere  ");
    sanitize(&mut padded);
    println!("Sanitized: '{}'", padded);
}

Bu kalipler arasinda secim yapmak basit bir sezgisel kurala dayanir: varsayilan olarak degismez odunc alma tercih edilir, degisiklik gerektiginde degistirilebilir odunc alma kullanilir ve yalnizca cagricinin degere artik ihtiyaci kalmadiginda sahiplik aktarilir. Bu yaklasim Rust temelleri rehberinde ayrintili olarak ele alinmaktadir.

Borrow Checker Hatalari ve Cozum Yollari

Borrow checker belirli hata kodlari uretir. En yaygin olanlari anlamak, sinir bozucu derleme hatalarini basit duzelmelere donusturur.

common_fixes.rsrust
fn main() {
    let mut scores = vec![90, 85, 78];
    let first = scores[0];
    scores.push(95);
    println!("First: {}, All: {:?}", first, scores);

    let name = String::from("Alice");
    let greeting = format!("Hello, {}", name);
    println!("{} says {}", name, greeting);

    let outer;
    {
        let inner = String::from("temporary");
        outer = inner;
    }
    println!("{}", outer);
}

Her duzeltme ayni ilkeye dayanir: kodu, odunc alma ve sahiplik yapisinin Rust kurallariyla uyumlu olacagi sekilde yeniden yapilandirmak. Borrow checker ile mucadele etmek genellikle diger dillerde hatalara yol acacak bir tasarim sorununa isaret eder. Esanlilik ve borrowing iceren daha ileri duzey kalipler icin Arc ve Mutex gibi paylasimli sahiplik tipleri vazgecilmez hale gelir.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Sonuc

  • Her Rust degerinin bir sahibi vardir; tip Copy implemente etmedikce atamada sahiplik aktarilir (move semantikleri)
  • Degismez referanslar (&T) paylasimli okuma erisimi saglar; degistirilebilir referanslar (&mut T) ozel yazma erisimini zorunlu kilar
  • Borrow checker, derleme zamaninda veri yarislarini ve sarkan referanslari sifir calisma zamani maliyetiyle onler
  • Lifetime'lar referans iliskilerini isaretler -- yeni kisitlamalar degil, var olan kisitlamalari tanimlar
  • Borrow checker kodu reddettikinde, unsafe'e basvurmak yerine sahiplik akisini yeniden yapilandirmak gerekir
  • Teknik mulakatlara hazirlik icin bu kaliplari Rust mulakat sorulari ile pratik yapmak akiciligi arttirir

Etiketler

#rust
#ownership
#borrowing
#memory-management
#systems-programming

Paylaş

İlgili makaleler