Drop Trait’i ile Temizlik Sırasında Kod Çalıştırmak
Akıllı işaretçi deseni için önemli ikinci trait Droptur. Bir değer kapsam
dışına çıkmak üzereyken ne olacağını özelleştirmenizi sağlar. Drop
uygulamasını herhangi bir tür için yazabilir ve böylece dosya, ağ bağlantısı
gibi kaynakları bırakmak için çalışacak kodu belirleyebilirsiniz.
Dropu akıllı işaretçiler bağlamında ele alıyoruz; çünkü Drop çoğunlukla
akıllı işaretçi yazarken kullanılır. Örneğin Box<T> bırakıldığında, kutunun
işaret ettiği öbek alanı serbest bırakılır.
Bazı dillerde, bazı türlerin örnekleriyle işiniz bittiğinde belleği ya da kaynağı serbest bırakacak kodu programcının elle çağırması gerekir. Dosya tanıtıcıları, soketler ve kilitler buna örnektir. Programcı unutursa sistem zorlanabilir hatta çökebilir. Rust’ta ise bir değer kapsam dışına çıktığında hangi kodun çalışacağını belirtirsiniz; derleyici bu kodu uygun yerlere otomatik ekler.
Bir değer kapsam dışına çıktığında çalışacak kodu Drop trait’ini
uygulayarak belirlersiniz. Bu trait, selfi değiştirilebilir referans olarak
alan drop adlı bir yöntem ister. Rust’ın dropu ne zaman çağırdığını görmek
için şimdilik bu yönteme println! ekleyelim.
Liste 15-14, kapsam dışına çıkınca mesaj basan OzelAkilliIsaretci yapısını
gösterir.
struct OzelAkilliIsaretci {
veri: String,
}
impl Drop for OzelAkilliIsaretci {
fn drop(&mut self) {
println!(
"`{}` verisine sahip OzelAkilliIsaretci bırakılıyor!",
self.veri
);
}
}
fn main() {
let c = OzelAkilliIsaretci {
veri: String::from("benim şeylerim"),
};
let d = OzelAkilliIsaretci {
veri: String::from("diğer şeyler"),
};
println!("OzelAkilliIsaretciler oluşturuldu");
}
Drop uygulamali OzelAkilliIsaretci yapisiDrop trait’i prelude içinde olduğu için ayrıca kapsamaya almamız gerekmez.
OzelAkilliIsaretci için Drop uyguluyor ve drop içinde bir mesaj
yazdırıyoruz. Gerçek hayatta bu gövdeye kaynak temizleme mantığı konurdu.
main içinde iki OzelAkilliIsaretci oluşturup ardından
OzelAkilliIsaretciler oluşturuldu yazdırıyoruz. main sonunda bu değerler
kapsam dışına çıkar ve Rust dropu otomatik çağırır. Yani dropu bizim
elle çağırmamıza gerek yoktur.
Programı çalıştırınca şunu görürüz:
$ cargo run
Compiling birak-ornegi v0.1.0 (file:///projects/birak-ornegi)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.60s
Running `target/debug/birak-ornegi`
OzelAkilliIsaretciler oluşturuldu
`diğer şeyler` verisine sahip OzelAkilliIsaretci bırakılıyor!
`benim şeylerim` verisine sahip OzelAkilliIsaretci bırakılıyor!
Rust değerler kapsam dışına çıktığında dropu otomatik çağırır. Değişkenler
oluşturulma sırasının tersine bırakılır; bu yüzden d, cden önce bırakılır.
Bazen bir değeri kapsam sonunu beklemeden daha erken temizlemek isteyebilirsiniz.
Örneğin kilit yöneten akıllı işaretçilerde, kilidi erkenden bırakıp aynı kapsam
içindeki başka kodun kilidi almasına izin vermek isteyebilirsiniz. Rust,
Drop trait’inin drop yöntemini elle çağırmanıza izin vermez; bunun yerine
standart kütüphanedeki std::mem::drop fonksiyonunu kullanırsınız.
Liste 15-14’teki maini değiştirip drop yöntemini elle çağırmak istersek,
Liste 15-15’teki kod çalışmaz.
struct OzelAkilliIsaretci {
veri: String,
}
impl Drop for OzelAkilliIsaretci {
fn drop(&mut self) {
println!(
"`{}` verisine sahip OzelAkilliIsaretci bırakılıyor!",
self.veri
);
}
}
fn main() {
let c = OzelAkilliIsaretci {
veri: String::from("bir miktar veri"),
};
println!("OzelAkilliIsaretci oluşturuldu");
c.drop();
println!("OzelAkilliIsaretci, main bitmeden önce bırakıldı");
}
Drop trait’indeki dropu elle cagirmaya calismakDerlersek şu hatayı alırız:
$ cargo run
Compiling birak-ornegi v0.1.0 (file:///projects/birak-ornegi)
error[E0040]: explicit use of destructor method
--> src/main.rs:16:7
|
16 | c.drop();
| ^^^^ explicit destructor calls not allowed
|
help: consider using `drop` function
|
16 - c.drop();
16 + drop(c);
|
For more information about this error, try `rustc --explain E0040`.
error: could not compile `birak-ornegi` (bin "birak-ornegi") due to 1 previous error
Hata açıkça drop çağrısının yasak olduğunu söyler. Buradaki destructor,
örneği temizleyen fonksiyon için kullanılan genel terimdir. Yapıcı
(constructor) nasıl örnek oluşturuyorsa, destructor da örneği temizler.
Rust buna izin vermez; çünkü kapsam sonunda yine otomatik drop çağrılırdı.
Böylece aynı değer iki kez temizlenmeye çalışılırdı.
Bu yüzden bir değeri erkenden bırakmak istiyorsak, std::mem::drop
fonksiyonunu çağırırız. Değeri fonksiyona argüman olarak veririz; prelude içinde
olduğu için ayrıca use yazmamız gerekmez. Liste 15-16 bunu gösterir.
struct OzelAkilliIsaretci {
veri: String,
}
impl Drop for OzelAkilliIsaretci {
fn drop(&mut self) {
println!(
"`{}` verisine sahip OzelAkilliIsaretci bırakılıyor!",
self.veri
);
}
}
fn main() {
let c = OzelAkilliIsaretci {
veri: String::from("bir miktar veri"),
};
println!("OzelAkilliIsaretci oluşturuldu");
drop(c);
println!("OzelAkilliIsaretci, main bitmeden önce bırakıldı");
}
std::mem::drop cagirmakBu kodun çıktısı şöyledir:
$ cargo run
Compiling birak-ornegi v0.1.0 (file:///projects/birak-ornegi)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.73s
Running `target/debug/birak-ornegi`
OzelAkilliIsaretci oluşturuldu
`bir miktar veri` verisine sahip OzelAkilliIsaretci bırakılıyor!
OzelAkilliIsaretci, main bitmeden önce bırakıldı
OzelAkilliIsaretci oluşturuldu ile main bitmeden önce bırakıldı satırları
arasında, bırakma mesajının yazdırılması c değerinin o noktada temizlendiğini
gösterir.
Drop uygulamasıyla verdiğiniz kodu birçok yaratıcı şekilde kullanabilirsiniz;
örneğin kendi bellek ayırıcınızı yazabilirsiniz. Drop ve Rust’ın sahiplik
sistemi sayesinde temizlik kodunu hatırlamak zorunda kalmazsınız; Rust bunu
sizin için yapar.
Ayrıca hâlâ kullanılan bir değeri yanlışlıkla temizlemekten de korkmanız
gerekmez. Referansların geçerliliğini koruyan sahiplik sistemi, dropun yalnızca
değer artık kullanılmıyorken bir kez çağrılmasını da sağlar.