RUST OWNERSHİP VE BORROWİNG NEDİR? DERLEYİCİ GARANTİLERİ NASIL ÇALIŞIR?
Rust’ı ilk kez deneyenlerin en hızlı fark ettiği şey, “çalıştırınca değil, derleyince” güven veren bir yaklaşım sunmasıdır. Bu güvenin merkezinde ownership (sahiplik) ve borrowing (ödünç alma) modeli yer alır: bellek hatalarını çoğu zaman daha kodu çalıştırmadan yakalar.
Birçok dilde bellek güvenliği; çöp toplayıcı, çalışma zamanı kontrolleri veya disiplinli kod incelemeleriyle sağlanır. Rust ise farklı bir yol izler: borrow checker kurallarıyla “kim, neyi, ne kadar süreyle kullanabilir?” sorusunu statik olarak çözer. Böylece sarkan referans (dangling reference), çift serbest bırakma (double free) ve veri yarışı (data race) gibi pahalı hatalar daha erken aşamada elenir.
Bu makalede “Rust ownership ve borrowing” mantığını günlük örneklerle açıklayacak; derleyicinin garantilerinin nasıl işlediğini, lifetimes’ın neden gerekli olduğunu ve pratikte sık görülen hataları nasıl çözeceğinizi adım adım ele alacağız.

Ownership modeli: değerlerin tek sahibi olması ne sağlar?
Rust’ta bir değer her zaman tek bir “sahip” değişken tarafından yönetilir. Bu sahiplik, değer kapsam dışına çıktığında kaynağın otomatik bırakılması anlamına gelir. Bu yaklaşım, RAII (Resource Acquisition Is Initialization) fikriyle uyumludur: kaynağı edinme ve bırakma yaşam döngüsüne bağlanır.
Stack ve heap: sahiplik neden özellikle heap’te önem kazanır?
Basit sayısal tipler genellikle stack’te taşınır ve kopyalanmaları ucuzdur. Ancak String, Vec gibi heap tahsis eden yapılar, yalnızca işaretçi değil; arka plandaki kapasite ve yaşam süresi yönetimi gibi sorumluluklar taşır. Rust, bu sorumluluğu tek bir sahipte topladığı için “kim serbest bırakacak?” belirsizliğini ortadan kaldırır.
Move semantics: kopyalamak yerine devretmek
Heap tabanlı bir değeri bir değişkenden diğerine atadığınızda Rust çoğu zaman kopyalamaz; move yapar. Yani sahiplik devredilir ve eski değişken geçersiz hale gelir. Bu davranış ilk başta “neden kullanamıyorum?” hissi yaratsa da, çift serbest bırakma gibi hataları kökten engeller.
Örneğin String move edildiğinde, eski değişkenin artık o belleği yönetmemesi gerekir. Derleyici bu yüzden eski değişkene erişimi reddeder. Eğer gerçekten kopya istiyorsanız, bunu açıkça clone() ile ifade etmeniz beklenir. Bu netlik, performans ve güvenlik dengesini bilinçli kurmanıza yardımcı olur.
Borrowing: kopyalamadan güvenli erişim nasıl mümkün olur?
Ownership her şeyin tek sahibi olması demekse, borrowing bu sahipliğe “geçici erişim” kapısı açar. Rust, referanslar üzerinden değerleri ödünç almanıza izin verir; fakat bunu belirli kurallarla sınırlar. Amaç basit: aynı anda hem paylaşım hem de değişiklik yaparken ortaya çıkan belirsizliği önlemek.
Immutable borrow: birden fazla okuma serbesttir
&T türüyle yaptığınız immutable borrow, bir değeri yalnızca okuma amaçlı ödünç alır. Bu durumda aynı anda birden fazla immutable referans almak mümkündür. Rust’ın burada hedeflediği, paylaşımı kolaylaştırırken değişikliği kontrol altında tutmaktır.
Mutable borrow: tek yazan kuralı
&mut T türüyle yapılan mutable borrow, değeri değiştirme yetkisi verir. Rust’ın kritik kuralı şudur: aynı kapsamda ya bir tane mutable referansınız olur ya da birden fazla immutable referansınız olur; ikisi aynı anda olamaz. Bu kural, veri yarışlarının temel sebebi olan “eşzamanlı yazma/okuma” karmaşasını daha tek iş parçacığında bile engeller.
fn main() {
let mut s = String::from("Merhaba");
let r1 = &s; // immutable borrow
let r2 = &s; // immutable borrow
// Aynı anda mutable borrow denemesi:
// let r3 = &mut s;
// println!("{}, {}", r1, r2);
}Yukarıdaki örnekte r1 ve r2 aynı anda okuma yapabildiği için sorun yoktur. Fakat aynı kapsamda &mut s üretmeye çalışırsanız derleyici bunu reddeder. Bu kısıt, ilk etapta “fazla katı” gibi görünse de; büyük kod tabanlarında beklenmedik yan etkileri azaltır ve refactor sürecini daha güvenli hale getirir.
Borrow checker: derleyici garantileri pratikte neyi kilitler?
Borrow checker, sahiplik ve ödünç alma kurallarının derleme zamanında uygulanmasını sağlar. Çoğu hata türü, aslında birkaç temel problemin farklı yüzleridir: bir kaynağın yaşam süresini yanlış varsaymak, aynı veriyi birden fazla yerden kontrolsüz değiştirmek veya referansların geçerliliğini kaybetmek.
Sarkan referansları engelleme
Bir fonksiyonun içinden, fonksiyon bitince yok olacak bir değere referans döndürmek birçok dilde tehlikelidir. Rust, referansların yaşam süresiyle ilgili koşulları kontrol ederek bu riski büyük ölçüde ortadan kaldırır. Eğer bir referansın, işaret ettiği veriden daha uzun yaşayabileceği bir senaryo oluşuyorsa derleyici kodu durdurur.
Veri yarışı ve aliasing sorunlarını azaltma
Rust, “aynı veriye birden fazla değiştirilebilir erişim” durumunu engelleyerek veri yarışını daha tasarım seviyesinde keser. Bu yalnızca çok iş parçacıklı kodlar için değil; tek iş parçacığında bile “bir yandan okurken bir yandan yazma” gibi belirsizlikleri ortadan kaldırır.
- Use-after-free riskini azaltır: değer kapsam dışına çıkınca erişim zorlaşır.
- Double free senaryosunu önler: sahiplik tek yerde tutulur.
- Data race olasılığını düşürür: eşzamanlı yazma/okuma kuralları netleşir.
- Refactor sırasında yan etkileri daha görünür kılar: erişim türü imzalara yansır.

Lifetimes: referansların “ne kadar yaşadığını” anlatmak
Lifetimes, Rust’ta çoğu zaman yanlış anlaşılan ama aslında çok pratik bir araçtır. Lifetime’lar “referansların geçerlilik aralığını” ifade eder. Önemli nokta: lifetime bir çalışma zamanı maliyeti değildir; derleyicinin tutarlılık analizi için kullandığı bir tür açıklamadır.
Ne zaman açık lifetime gerekir?
Bir fonksiyon birden fazla referans alıp bir referans döndürdüğünde, derleyici “çıktı referansı hangisiyle ilişkili?” sorusunu netleştirmek isteyebilir. Basit durumlarda lifetime elision kuralları devreye girer ve yazmanız gerekmez. Ancak bazı imzalarda ilişkiyi açıkça belirtmek gerekir.
fn en_uzun<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() { x } else { y }
}
fn main() {
let a = String::from("Rust");
let b = String::from("Ownership");
let sonuc = en_uzun(a.as_str(), b.as_str());
println!("{}", sonuc);
}Burada 'a, dönen referansın x ve y ile aynı geçerlilik aralığına bağlı olduğunu ifade eder. Bu sayede derleyici, dönen referansın işaret ettiği verinin yaşam süresini güvenli biçimde izleyebilir.
Lifetime’lar neden “sihirli” değil?
Lifetimes, veriyi uzatmaz veya kopyalamaz; yalnızca “bu referans şunlardan daha uzun yaşayamaz” gibi sınırlar koyar. Bu yüzden lifetime hatası aldığınızda çoğu zaman çözüm; veriyi taşımak (move), sahipliği yeniden düzenlemek, gerekirse String gibi sahip olunan türlere dönmek ya da API tasarımını sadeleştirmektir.
Günlük Rust kodunda ownership ve borrowing desenleri
Teorik kuralların ötesinde, Rust’ta üretken olmanızı sağlayan şey tekrar eden pratik desenlerdir. İyi haber şu: birkaç alışkanlık kazanınca, borrowing çoğu zaman “doğal” hale gelir ve hata mesajları daha anlamlı görünmeye başlar.
Slice ve iterator kullanımıyla kopyasız çalışma
&[T] slice ve iterator’lar, koleksiyonların tamamını kopyalamadan işlem yapmayı kolaylaştırır. Bir fonksiyonun parametresini &Vec<T> yerine &[T] almak, API’yi daha esnek ve daha az bağımlı hale getirir. Benzer şekilde &str ile çalışmak, gereksiz String tahsislerini azaltır.
Struct metodlarında &self ve &mut self seçimi
Metod imzaları, erişim niyetini açık eder. Sadece okuyan metodlar &self ile tanımlanır; iç durumu değiştirenler &mut self ister. Bu ayrım, kodu okuyan kişiye de “bu çağrı yan etki üretir mi?” sorusunun cevabını hızlıca verir.
Sahip olunan tür mü, referans mı?
Bir API tasarlarken “bu fonksiyon veriyi sahiplenmeli mi, ödünç mü almalı?” sorusu kritik olur. Genel kural: işlem kısa süreli ve kopyalamaya gerek yoksa referans; veri fonksiyon sınırlarını aşacaksa veya saklanacaksa sahip olunan türler tercih edilir. Bu dengeyi oturtmak, Rust’ta ergonomiyi belirleyen temel noktalardan biridir.
Daha sistematik örnekler ve uygulamalı egzersizlerle bu modeli içselleştirmek için Rust Eğitimi sayfasına göz atabilirsiniz. Özellikle gerçek projelerde lifetimes ve borrow checker mesajlarını okumayı öğrenmek, hızınızı belirgin şekilde artırır.
Sık görülen derleyici hataları ve çözüm stratejileri
Borrow checker hataları, çoğu zaman tek bir noktadan kaynaklanır: “aynı anda fazla şey yapmak.” Rust, bunu erken fark ettirir. Aşağıdaki stratejiler, pratikte en çok işe yarayan yaklaşımlardır.
“Borrowed value does not live long enough”
Bu hata genellikle kısa ömürlü bir değere daha uzun ömürlü bir referans üretmeye çalıştığınızda çıkar. Çözüm; referans döndürmek yerine sahip olunan bir değer döndürmek, ya da veriyi daha uzun yaşayan bir kapsamda üretmektir. Bazen de, değeri bir yapının alanı yapmak ve yaşam süresini yapıya bağlamak gerekir.
“Cannot borrow as mutable because it is also borrowed as immutable”
Aynı kapsamda hem okuma hem yazma erişiminin çakıştığını gösterir. Çözüm seçenekleri: immutable borrow’un kullanımını daha erken bitirmek (blok içine almak), kodu iki aşamaya bölmek, ya da veriyi yeniden düzenleyerek aynı anda iki farklı erişim ihtiyacını ortadan kaldırmaktır.
Clone mı, redesign mı?
Bazı durumlarda clone() pratik bir çıkış olabilir; fakat körlemesine clone etmek performansı ve bellek kullanımını şişirebilir. En iyi yaklaşım, önce veri akışını anlamak: veri gerçekten kopyalanmak zorunda mı, yoksa borrow süreleri düzenlenerek mi çözülür? Gerekiyorsa küçük kopyalar yapmak bazen doğru seçimdir; önemli olan bunun bilinçli olmasıdır.

Derleyici garantilerini zihinde canlandırmak: basit bir kontrol listesi
Ownership ve borrowing’i akılda tutmanın pratik yolu, her değişkende şu soruları sormaktır: “Bu değerin sahibi kim?”, “Şu an kim erişiyor?”, “Bu erişim okuma mı yazma mı?” ve “Bu erişim ne zaman bitiyor?” Bu sorulara net yanıt verebiliyorsanız, borrow checker genellikle sizinle aynı fikirde olur.
Rust, güvenliği “disiplin” olarak değil, derleyiciyle yapılan bir anlaşma olarak sunar. Siz erişim niyetinizi açık edersiniz; derleyici de bellek güvenliği ve yarış durumları konusunda güçlü garantiler verir. Sonuçta ortaya, çoğu hatayı üretim ortamında değil, derleme aşamasında yakalayan daha sağlam bir sistem çıkar.
Bu modeli öğrendikten sonra Rust’ta kod yazmak, ilk günlerdeki “kısıtlama” hissinden çıkıp “netlik” hissine dönüşür: hangi verinin nerede yaşadığını, kim tarafından değiştirildiğini ve ne zaman serbest bırakıldığını daha iyi görürsünüz. Bu görünürlük, özellikle performans kritik ve güvenlik hassas projelerde büyük bir avantajdır.


