Rust'ta ownership ve borrowing kavramları nasıl çalışıyor? Bellek güvenliğini sağlamak için derleyici hangi kuralları uyguluyor, mutable ve immutable referanslar arasındaki farklar neler? Bu mekanizmaların runtime'da bir etkisi var mı, yoksa tamamen compile-time mi kontrol ediliyor? Sizce bu model diğer dillerdeki benzer yaklaşımlardan ne kadar ayrılıyor?
Rust'ta Ownership ve Borrowing Nasıl Çalışır?
👁️ 74 görüntüleme💬 2 cevap❤️ 0 beğeni
2 Cevap
Kanka, ben de geçen ay bir proje için Rust’a geçtim ve en çok takıldığım şey ownership kuralları oldu. İlk başta, bir değişkeni fonksiyona pas geçince o değişkeni bir daha kullanamıyordum; derleyici “value moved” hatası verirken valla kafam karışıyordu. Çözüm, ya `&` ile immutable referans, ya da `&mut` ile mutable referans vermekti. Immutable referanslar aynı anda birden fazla olabilir, ama içeriği değiştiremezsin; mutable referans ise tek başına bulunmalı, yani aynı anda başka bir referans olamaz. Bu kurallar sayesinde veri yarışları compile‑time’da tamamen önleniyor; runtime’da ekstra bir kontrol ya da kilit mekanizması yok, yani performans kaybı da yaşamıyorsun.
Benim en büyük “aha!” anım, bir veri yapısını (örnek: `Vec<T>`) bir fonksiyona `&mut` olarak geçirirken, fonksiyon içinde başka bir `&` alıp aynı veriyi okuttuğumda derleyicinin “cannot borrow `x` as immutable because it is also borrowed as mutable” hatasını vermesiydi. Bu, C++’ta aklıma gelen “dangling pointer” ya da “data race” problemlerine kıyasla çok daha temiz bir deneyim sundu. Diğer dillerde genelde bu tür kontrolleri runtime’da lock’lar ya da garbage collector üzerinden yapıyor, ama Rust’ın modeli compile‑time’da kuralları zorlayarak güvenliği garantiliyor; kesinlikle “başka bir şey eklemeden” bellek güvenliği sağlamak gibi bir avantaj. Bu yüzden, Rust’ın ownership/borrowing sistemi diğer dillere göre çok daha katı ama bir o kadar da etkili.
Rust’ta ownership‑borrowing sistemi aslında derleyicinin “hafıza çalakalığını” compile‑time’da çözdüğü bir yapı. Kısacası bir değişkenin sahibi sadece bir yerden olabilir, bu da `move` ile başka bir yere aktarılırsa orijinali artık kullanılamaz demek. Borrowing ise bu sahipliliği geçici olarak “ödünç alıyor”. Immutable referans (`&T`) birden çok olabilir, veri değişmez; mutable referans (`&mut T`) ise sadece tek başına bulunabilir ve aynı anda hem mutable hem de immutable referans olmaz. Derleyici bu kuralları “borrow checker” ile kontrol eder; runtime’da ekstra bir check yok, yani performans kaybı da yok. Mutable ve immutable referansların bu katı ayrımı, C++’daki raw pointer ya da Java’nın garbage‑collector temelli referanslarından farklı; C++’ta UB (undefined behavior) riski yüksek, Java’da ise runtime’da GC ve null‑check’ler var. Rust’ta bu model sayesinde veri yarışları (data race) compile‑time’da yakalanıyor, bu yüzden paralel kod yazmak daha güvenli oluyor. Ben de bir projede `Arc<Mutex<T>>` ile çok thread’li bir yapı kurarken, borrow checker’ın “bu anda sadece bir mutable referans var” uyarısını görmem, hataları çok erken aşamada yakalamamı sağladı; valla işin içine runtime’da garip hatalar girmiyor. Kısacası, model tamamen compile‑time’da çalışıyor ve diğer dillerdeki “trust me” ya da “runtime safety” yaklaşımlarından bayağı ayrılıyor.