Drop
Tanımı ile Temizleme Üzerinde Kod Çalıştırma
Akıllı işaretçi modeli için önemli olan ikinci tanım, bir değer kapsam dışına çıkmak üzereyken ne olacağını
özelleştirmenize olanak tanıyan Drop
'tur. Herhangi bir tür üzerinde Drop
için bir sürekleme sağlayabilirsiniz
ve bu kod, dosyalar veya ağ bağlantıları gibi kaynakları serbest bırakmak için kullanılabilir.
Drop
'u akıllı işaretçiler bağlamında tanıtıyoruz çünkü Drop
'un işlevselliği neredeyse her zaman bir akıllı
işaretçi uygulanırken kullanılır. Örneğin, bir Box<T>
bırakıldığında, yığın üzerinde kutunun işaret ettiği alan
belleğe iade edilir.
Bazı dillerde, bazı türler için, programcı bu türlerin bir örneğini kullanmayı her bitirdiğinde belleği veya kaynakları boşaltmak için kod çağırmalıdır. Örnekler arasında dosya tutamaçları, soketler veya kilitler yer alır. Eğer unuturlarsa, sistem aşırı yüklenebilir ve çökebilir. Rust'ta, bir değer kapsam dışına çıktığında belirli bir kod parçasının çalıştırılmasını belirtebilirsiniz ve derleyici bu kodu otomatik olarak ekleyecektir. Sonuç olarak, belirli bir türün bir örneğinin bittiği bir programın her yerine temizleme kodu yerleştirme konusunda dikkatli olmanız gerekmez—yine de kaynak sızdırmazsınız!
Drop
tanımını sürekleyerek bir değer kapsam dışına çıktığında çalıştırılacak kodu belirlersiniz.
Drop
, self
öğesine değiştirilebilir bir referans alan drop
adlı bir metodu tanımlamanızı gerektirir.
Rust'ın drop
'u ne zaman çağırdığını görmek için şimdilik drop
'u println!
ifade yapılarıyla kullanalım.
Liste 15-14, Rust'ın drop
fonksiyonunu ne zaman çalıştırdığını göstermek için, örnek kapsam dışına çıktığında
Dropping CustomSmartPointer!
yazdıracak olan tek gizli fonksiyonu olan bir CustomSmartPointer
yapısını gösterir.
Dosya adı: src/main.rs
struct CustomSmartPointer { data: String, } impl Drop for CustomSmartPointer { fn drop(&mut self) { println!("Dropping CustomSmartPointer with data `{}`!", self.data); } } fn main() { let c = CustomSmartPointer { data: String::from("my stuff"), }; let d = CustomSmartPointer { data: String::from("other stuff"), }; println!("CustomSmartPointers created."); }
Drop
tanımı genel Rust yapısına dahil edilmiştir, bu nedenle onu kapsam içine almamıza gerek yoktur.
CustomSmartPointer
üzerinde Drop
'u sürekliyoruz ve println!
çağrısı yapan drop
metodu için bir tanımlama sağlıyoruz.
Drop
fonksiyonunun gövdesi, türünüzün bir örneği kapsam dışına çıktığında çalıştırmak istediğiniz herhangi bir mantığı yerleştireceğiniz yerdir.
Rust'ın drop
'u ne zaman çağıracağını görsel olarak göstermek için burada bazı metinler yazdırıyoruz.
main
'de, iki CustomSmartPointer
örneği oluşturuyoruz ve ardından oluşturulan CustomSmartPointer
'ları yazdırıyoruz.
main
'in sonunda, CustomSmartPointer
örneklerimiz kapsam dışına çıkacak ve Rust, drop
'a koyduğumuz kodu çağırarak son
mesajımızı yazdıracak. drop
metodunu açıkça çağırmamıza gerek olmadığına dikkat edin.
Bu programı çalıştırdığımızda aşağıdaki çıktıyı göreceğiz:
$ cargo run
Compiling drop-example v0.1.0 (file:///projects/drop-example)
Finished dev [unoptimized + debuginfo] target(s) in 0.60s
Running `target/debug/drop-example`
CustomSmartPointers created.
Dropping CustomSmartPointer with data `other stuff`!
Dropping CustomSmartPointer with data `my stuff`!
Örneklerimiz kapsam dışına çıktığında Rust bizim için otomatik olarak drop
'u çağırdı ve belirttiğimiz kodu çalıştırdı.
Değişkenler oluşturulma sıralarının tersine bırakılır, bu nedenle d
, c
'den önce bırakılır. Bu örneğin amacı,
drop
metodunun nasıl çalıştığına dair görsel bir kılavuz sunmaktır; genellikle bir yazdırma mesajı yerine türünüzün çalışması gereken
temizleme kodunu belirtirsiniz.
std::mem::drop
ile Bir Değeri Erken Bırakma
Ne yazık ki, otomatik drop
fonksiyonunu devre dışı bırakmak kolay değildir. drop
'u devre dışı bırakmak genellikle gerekli değildir;
Drop
tanımının tüm amacı bunun otomatik olarak halledilmesidir. Ancak bazen, bir değeri erkenden temizlemek isteyebilirsiniz.
Buna bir örnek, kilitleri yöneten akıllı işaretçiler kullanırken verilebilir: kilidi serbest bırakan drop
metodunu zorlamak isteyebilirsiniz,
böylece aynı kapsamdaki diğer kodlar kilidi alabilir. Rust, Drop
'un drop
metodunu manuel olarak çağırmanıza izin vermez;
bunun yerine, bir değeri kapsamının sonundan önce bırakılmaya zorlamak istiyorsanız standart kütüphane tarafından sağlanan
std::mem::drop
işlevini çağırmanız gerekir.
Liste 15-15'te gösterildiği gibi, Liste 15-14'teki main
fonksiyonunu değiştirerek Drop
'un drop
metodunu manuel olarak çağırmaya çalışırsak,
bir derleyici hatası alırız:
Dosya adı: src/main.rs
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("some data"),
};
println!("CustomSmartPointer created.");
c.drop();
println!("CustomSmartPointer dropped before the end of main.");
}
Bu kodu derlemeye çalıştığımızda alacağımız hata budur:
$ cargo run
Compiling drop-example v0.1.0 (file:///projects/drop-example)
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: `drop(c)`
For more information about this error, try `rustc --explain E0040`.
error: could not compile `drop-example` due to previous error
Bu hata mesajı, drop
'u açıkça çağırmamıza izin verilmediğini belirtir. Hata mesajı, bir örneği temizleyen bir
fonksiyon için genel programlama terimi olan yıkıcı terimini kullanır. Bir yıkıcı, bir örnek oluşturan bir yapıcıya benzer.
Rust'taki drop
fonksiyonu belirli bir yıkıcıdır.
Rust, drop
'u açıkça çağırmamıza izin vermez, çünkü Rust yine de main
'in sonundaki değer üzerinde otomatik olarak drop
'u çağıracaktır.
Bu, Rust aynı değeri iki kez temizlemeye çalışacağı için çift serbest bırakma hatasına neden olur.
Bir değer kapsam dışına çıktığında drop
'un otomatik olarak eklenmesini devre dışı bırakamayız ve drop
metodunu açıkça çağıramayız.
Bu nedenle, bir değeri erken temizlenmeye zorlamamız gerekiyorsa, std::mem::drop
fonksiyonunu kullanırız.
std::mem::drop
fonksiyonu Drop
tanımındaki drop
metodundan farklıdır. Düşürmeye zorlamak istediğimiz değeri argüman olarak ileterek çağırırız.
Fonksiyon genel Rust yapısındadır, bu nedenle Liste 15-15'teki main
'i, Liste 15-16'da gösterildiği gibi drop
fonksiyonunu çağıracak şekilde
değiştirebiliriz:
Dosya adı: src/main.rs
struct CustomSmartPointer { data: String, } impl Drop for CustomSmartPointer { fn drop(&mut self) { println!("Dropping CustomSmartPointer with data `{}`!", self.data); } } fn main() { let c = CustomSmartPointer { data: String::from("some data"), }; println!("CustomSmartPointer created."); drop(c); println!("CustomSmartPointer dropped before the end of main."); }
Bu kodu çalıştırmak aşağıdakileri yazdıracaktır:
$ cargo run
Compiling drop-example v0.1.0 (file:///projects/drop-example)
Finished dev [unoptimized + debuginfo] target(s) in 0.73s
Running `target/debug/drop-example`
CustomSmartPointer created.
Dropping CustomSmartPointer with data `some data`!
CustomSmartPointer dropped before the end of main.
CustomSmartPointer
oluşturulduğu esnada Dropping CustomSmartPointer with data (some data)!
metni yazdırılır ve
CustomSmartPointer dropped before the end of main.
metni, drop
metod kodunun bu noktada c
'yi bırakmak için çağrıldığını gösterir.
Drop
tanım süreklemesinde belirtilen kodu, temizlemeyi kolay ve güvenli hale getirmek için birçok şekilde kullanabilirsiniz:
örneğin, kendi bellek ayırıcınızı oluşturmak için kullanabilirsiniz! Drop
tanımı ve Rust'ın sahiplik sistemi sayesinde,
temizlemeyi hatırlamak zorunda kalmazsınız çünkü Rust bunu otomatik olarak yapar.
Ayrıca, hala kullanımda olan değerlerin yanlışlıkla temizlenmesinden kaynaklanan sorunlar hakkında endişelenmenize de gerek yoktur:
referansların her zaman geçerli olmasını sağlayan sahiplik sistemi, drop
'un değer artık kullanılmadığında yalnızca bir kez çağrılmasını da sağlar.
Box<T>
'yi ve akıllı işaretçilerin bazı özelliklerini incelediğimize göre, şimdi standart kütüphanede tanımlanan diğer birkaç akıllı
işaretçiye bakalım.