ENESENES

Rust Ownership Nedir? Stack Bellek ve Heap Bellek

@root

ben kimim

10696 minute

Rust Ownership Nedir? Stack Bellek ve Heap Bellek

Stack Bellek ve Heap Bellek

Genelde programlama dillerinde bu iki kavramın bilinmesine gerek yoktur fakat ne zaman ki biraz low-level'a yaklaşırmaya başlarsanız peşinizi bırakmayacak kavramlar haline gelir. İşte biz de şu an o durumlardan birini işleyeceğemiz için sistem programlama dillerinde -C/C++, Rust gibi- bu kavramları bilmeliyiz. Rust'ta Ownership olayını anlamak için de öncelikle yazımıza stack ve heap bellekten başlamalıyız. Stack bellek ve heap bellek, programlama dillerinde verilerin depolanması için kullanılan iki temel bellek alanı olarak adlandırılmıştır. Bu iki bellek türündeki temel fark aslında bellek yönetiminin nasıl yapıldığından kaynaklanmaktadır. Her ikisinin de özellikleri aşağıda verilmiştir.

Stack Bellek:

  • Genellikle yerel değişkenler ve fonksiyon çağrıları gibi kısa ömürlü verileri depolamak için kullanılır.
  • Verilerin yaşam süresi, değişkenin tanımlandığı blok süresiyle sınırlıdır. Blok sona erdiğinde, stack bellekteki veri otomatik olarak serbest bırakılır.
  • Bellek yönetimi otomatik olarak gerçekleşir ve genellikle hızlıdır.
  • Bellek boyutu genellikle sınırlıdır ve sabittir.
Stack bellek, derlemeden önce boyutu belli olan verilerin aldığı değerleri sırayla saklar ve değerleri ters şekilde kaldırır. Basitçe son giren ilk çıkar mantığı -LIFO (Last-In-First-Out)- diyebiliriz. Örnek verecek olursak üst üste dizdiğimiz kitaplardan okumak istediğimizi en yukarıdan alıp okuma gibi düşünebilirsiniz.

Heap Bellek:

  • Dinamik bellek tahsisi ve serbest bırakma işlemleri için kullanılır.
  • Verilerin yaşam süresi program tarafından kontrol edilir ve genellikle dinamik olarak değişir.
  • Bellek yönetimi programcı tarafından manuel olarak yapılır (malloc, free gibi fonksiyonlar kullanılarak).
  • Bellek boyutu, genellikle programın ihtiyacına göre dinamik olarak büyütülür.

Heap bellek diğer belleğin aksine düzensizdir, sıra yoktur. Heap belleğe veri koyduğunuzda belirli bir miktar yer tahsis edersiniz. Heap bellekte yeterince büyük bir alan bulunur ve buradan kullanımda olanlar konum adresi olan bir pointer geri döndürür. Bu işleme yer tahsis etme denir. Fark ettiğiniz üzere stack bellekte derlenemden önce belliyken boyutumuz heap bellekte işler bu şekilde işlemiyor, değişiyor.

Ownership Kavramı

Ownership kavramı ise bu heap bellekte eğer iki pointer da aynı yeri gösterirse kavramından çıkmıştır. Çünkü birinde olan değişiklik diğerini de etkileyecektir. Bahsedilen durum memoryde ilerde hatalar/güvenlik açıkları barındırabilir hale gelme ihtimali vardır. C/C++, bunu kontrol etmediğinden tamamen yazılımcının sorumluluğuna bırakmıştır. Rust ile C/C++ bu konuda farklı yaklaşım sergilemektedir dolayısıyla Rust memory güvenlik konusuna odaklandığından bunu engellemektedir. Bu yüzden owner/ownership kavramı ortaya çıkmıştır. Öncelikle ownership kurallarına bakmamız gerekiyor. Aşağıda verilecek örnekleri de bu kurallar bazında vereceğim.

  • Rust'taki her değerin bir owner'ı vardır.
  • Aynı anda yalnızca bir owner olabilir.
  • Owner kapsam dışına çıktığında, değer düşürülür(drop).

Ownership Örneklerle Anlatımı

Aşağıdaki kodda string türündeki s1 değişkeni atadığımızda bellekte nasıl olacağına bakalım.


fn main() {
    let string1 = String::from("hello");
    println!("{}, world!", string1);
}

Bu string bellekte şu şekilde tutulur:

Aşağıdaki kodda string türündeki s1 değişkeni atadığımız da bellekte nasıl olacağına bakalım.


fn main() {
    let string1 = String::from("hello");
    let string2 = string1;
    println!("{}, world!", string1);
}

Bu kod 'borrow of moved value: `string1`' hatası vererek çalışmayacaktır ve string1 droplanacaktır. Bunu da şu şekilde gösterebiliriz:

Görüldüğü üzere Rust bizim için bunu engelliyor. Peki biz gerekirse bunu aynı anda nasıl kullanabiliriz? Onun için de şu şekilde klasik yöntemden gidiyoruz. string1'in içeriğini string2'ye klonluyoruz.


fn main() {
    let string1 = String::from("hello");
    let string2 = string1.clone();
    println!("{}, world!", string1);
    println!("{}, world!", string2);
}

Tabii söylediğimiz gibi bu durum heap bellek için geçerlidir. Onun dışında primitive data türlerinde buna rastlamazsınız. Örneğin aşağıdaki kod hatasız bir şekilde çalışacaktır.


fn main() {
    let int1:i16 = 19;
    let int2:i16 = 23;
    println!("x:{}, y:{}", int1, int2);
}

Sonuç

Bugün Rust'ta anlaşılması zor ama eğlenceli bir konuya değindim. Umarım anlatabilmişimdir ;)

Kaynak:

Discussion (0)

No comments