Rust'ın Gizemli Kutusu: Box<T> Tipi ve Diğer Dillerle Karşılaştırmalı İnceleme
[en] Read in English 2023-07-11
Rust dilinin sunduğu birçok özellik arasında, “Box” tipi en önemli olanlarından biridir. Yığın (stack) üzerinde hafıza sınırlamalarına sahip nesnelerin dinamik olarak ayrılan yığın (heap) üzerinde saklanmasına izin verir. Bu makalede, Rust dilinin Box tipini detaylı olarak inceleyeceğiz ve özelliklerini C#, Go ve Java dilleriyle karşılaştıracağız.
Rust’ta Box Tipi
Rust dilinde, Box tipi, verinin yığın (heap) üzerinde ayrılan hafızada saklanması gerektiğinde kullanılır. Bu, veri boyutunun derleme zamanında bilinmediği durumlarda veya verinin boyutunun çok büyük olduğu durumlarda kullanılır. Box tipi, derleme sürecinde otomatik olarak düşen bir akıllı işaretçidir (smart pointer). Yani, Box kullanıldığında, Rust hafıza güvenliğini ve bellek sızıntısını önler.
Box kullanımına basit bir örnek aşağıdadır:
fn main() {
let b = Box::new(5);
println!("b = {}", *b);
}
Bu örnekte, 5 değerine sahip bir tam sayı kutulanır ve b değişkenine atanır. Box değeri, *
operatörü ile dereference edilebilir.
Rust’ın Box<T>
tipinin çeşitli kullanım örneklerine daha derinlemesine bakalım:
Büyük Veri Yapıları
Bir veri yapısının boyutu çok büyük olduğunda, Rust’ta Box<T>
kullanmak genellikle en iyi seçenektir.
fn main() {
let big_data = Box::new([0; 1_000_000]);
println!("{}", big_data[0]);
}
Bu örnekte, Box<T>
kullanılmazsa, yığının (stack) kapasitesini aşabiliriz. Rust buna izin vermez ve bir hata oluşturur. Ancak, Box<T>
kullanarak, veriyi heap’te saklayabilir ve böylece bu sorunu çözebiliriz.
Recursive Tip Tanımları
Rust’ın Box<T>
tipi, recursive veri yapıları oluştururken de sıklıkla kullanılır. En basit örneği, bir ağaç yapısıdır.
enum Tree {
Empty,
Node(i32, Box<Tree>, Box<Tree>),
}
fn main() {
let left = Tree::Node(5, Box::new(Tree::Empty), Box::new(Tree::Empty));
let tree = Tree::Node(10, Box::new(left), Box::new(Tree::Empty));
}
Bu örnekte, her düğüm Node(i32, Box<Tree>, Box<Tree>)
şeklinde tanımlanır. Bu, her düğümün bir değeri ve iki çocuğu olduğunu belirtir. Çocuklar, Box<T>
tipindeki başka bir ağaçtır. Burada, Box<T>
kullanımının nedeni, Tree
enum’unun boyutunun, Node
değerlerinin potansiyel boyutu nedeniyle bilinmemesidir. Box<T>
ile, boyutu bilinmeyen değerleri güvenli bir şekilde kullanabiliriz.
Trait Nesneleri
Rust’ın Box<T>
tipi, bir trait nesnesi oluşturmak için de kullanılabilir. Trait nesneleri, birden çok tipin aynı traiti paylaştığı durumlarda kullanılır.
trait Animal {
fn make_noise(&self);
}
struct Dog;
impl Animal for Dog {
fn make_noise(&self) {
println!("Woof!");
}
}
struct Cat;
impl Animal for Cat {
fn make_noise(&self) {
println!("Meow!");
}
}
fn main() {
let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog),
Box::new(Cat),
];
for animal in animals {
animal.make_noise();
}
}
Bu örnekte, Animal
adında bir trait tanımlıyoruz ve Dog
ve Cat
structları bu traiti uyguluyor. Daha sonra bir Animal
vektörü oluşturup içerisine Dog
ve Cat
ekliyoruz. Bu, farklı türlerde nesneleri aynı koleksiyonda tutmamıza olanak sağlar. Box<dyn Trait>
kullanımı, runtime’da farklı türlerin aynı traiti uygulayabilmesini sağlar.
C#‘ta Box Kavramı
C# dilinde, Box tipi doğrudan yoktur. Ancak, boxing ve unboxing kavramları vardır. Boxing işlemi, bir değer tipini (örneğin, bir tam sayı) referans tipine (örneğin, bir nesneye) dönüştürme sürecidir. Unboxing ise tam tersidir.
Örnek:
int i = 123;
object o = i; // boxing
int j = (int)o; // unboxing
C#‘ta, boxing ve unboxing işlemleri performans maliyeti taşır. Rust’ın aksine, hafıza güvenliğini ve bellek sızıntısını önlemek için belirli bir mekanizma sağlamaz.
Go’da Box Kavramı
Go dilinde, Rust’ın Box tipine karşılık gelen bir mekanizma yoktur. Ancak, Go’da bir nesnenin pointer’ını oluşturabilir ve bu şekilde dinamik hafıza tahsisi yapabilirsiniz.
i := new(int)
*i = 5
fmt.Println(*i)
Go dilinde garbage collector bulunur. Bu nedenle, kullanılmayan hafıza otomatik olarak geri kazanılır.
Java’da Box Kavramı
Java’da, ilkel tipleri nesne tiplerine dönüştürme işlemine “boxing” denir. Bu, önceden tanımlanmış sarmalayıcı sınıflar kullanılarak yapılır. Örneğin, int
tipi Integer
tipine dönüştürülebilir. Java’da, otomatik boxing ve unboxing mekanizması vardır.
Integer boxedInt = Integer.valueOf(5);
int unboxedInt = boxedInt.intValue();
Java dilinde garbage collector bulunur ve bu sayede kullanılmayan hafıza otomatik olarak geri kazanılır.
Sonuç
Rust dilinin Box tipi, dinamik hafıza tahsisi için güçlü bir araçtır. Aynı zamanda otomatik bellek yönetimi sağlar ve hafıza güvenliğini garantiler. C#, Go ve Java dillerinde, bu kavramlar farklı şekillerde ele alınmıştır. Genel olarak, Rust dilinin Box tipi, C#‘nın boxing/unboxing mekanizmasına ve Go’nun pointer kavramına kıyasla daha güvenli ve etkilidir. Java’da otomatik boxing/unboxing işlemleri ve garbage collector mekanizması Rust’ın Box tipiyle benzer avantajlar sunar, ancak Rust, derleme zamanında hafıza güvenliğini garantilerken Java runtime’da garbage collector’a dayanır.