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.");
}

Liste 15-14: Temizleme kodumuzu koyacağımız Drop'u sürekleyen bir CustomSmartPointer yapısı

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.");
}

Liste 15-15: Erken temizlemek için Drop tanımından drop metodunu manuel olarak çağırma girişimi

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.");
}

Liste 15-16: Bir değeri kapsam dışına çıkmadan önce açıkça bırakmak için std::mem::drop çağrısı

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.