Ana içeriğe geç

ch15-04-rc

Rc, Referans Sayımlı Akıllı İşaretçi

Çoğu durumda, sahiplik açıktır: bir değişkenin belirli bir değerin sahibi olduğunu tam olarak bilirsiniz. Ancak, tek bir değerin birden fazla sahibi olabileceği durumlar da vardır. Örneğin, grafik veri yapılarında, birden fazla kenar aynı düğüme işaret edebilir ve o düğüm, ona işaret eden tüm kenarlar tarafından kavramsal olarak sahip olunmaktadır.

ipucu

Bir düğüm, ona işaret eden hiçbir kenar kalmadığında ve dolayısıyla sahibi olmadığında temizlenmelidir.

Rust türü Rc'yi kullanarak birden fazla sahipliği açıkça etkinleştirmeniz gerekir; bu, referans sayımı için bir kısaltmadır. Rc türü, bir değere olan referansların sayısını takip ederek, değerin hala kullanılıp kullanılmadığını belirler. Bir değere sıfır referans varsa, değer, referansların geçersiz hale gelmemesi için temizlenebilir.

Rc'yi, bir aile odasındaki bir televizyon olarak düşünün. Bir kişi TV izlemeye geldiğinde, TV'yi açar. Diğerleri odaya girip TV'yi izleyebilir. Son kişi odadan çıktığında, TV'yi kapatır çünkü artık kullanılmamaktadır. Eğer birisi TV'yi kapatırsa ve diğerleri hala izliyorsa, kalan TV izleyicilerinden büyük bir rahatsızlık duyulur!
— Rust Hatırlatması

Rc türünü, veriyi yığın üzerinde birden fazla program parçasının okuyabilmesi için tahsis etmek istediğimizde kullanırız ve derleme zamanında hangi parçanın veriyi en son kullanacağını belirleyemeyiz. Hangi parçanın en son bitireceğini biliyorsak, o kısmı verinin sahibi yapabiliriz ve normal sahiplik kuralları derleme zamanında geçerli olur.

tehlike

Rc'nin yalnızca tek iş parçacıklı senaryolar için kullanıldığını unutmayın. Bölüm 16'da eşzamanlılığı tartıştığımızda, çok iş parçacıklı programlarda referans sayımı nasıl yapılacağını ele alacağız.

Verileri Paylaşmak için Rc Kullanımı

Liste 15-5'teki cons listesi örneğine dönelim. Bunu Box kullanarak tanımladığımızı hatırlayın. Bu sefer, üçüncü bir listenin sahipliğini paylaşan iki liste oluşturacağız. Kavramsal olarak, bu, Şekil 15-3'e benzer görünüyor:

Şekil 15-3: a adında üçüncü bir listenin sahipliğini paylaşan b ve c adında iki liste

Öncelikle 5 ve ardından 10 içeren liste ayı oluşturacağız. Sonra 3 ile başlayan b ve 4 ile başlayan c adında iki daha liste oluşturacağız. Hem b hem de c listeleri, 5 ve 10 içeren ilk a listesinin devamı olacak. Diğer bir deyişle, her iki liste de 5 ve 10 içeren ilk listeyi paylaşacak.

Bu senaryoyu, Box ile tanımladığımız List ile uygulamaya çalışmak çalışmayacaktır, aşağıdaki Liste 15-17'de gösterildiği gibi:

` ile iki liste olamayacağını gösterme">

{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-17/src/main.rs}}

Bu kodu derlediğimizde, bu hatayı alırız:

{{#include ../listings/ch15-smart-pointers/listing-15-17/output.txt}}

Cons varyantları, tuttukları verilerin sahibidir, bu yüzden b listesini oluşturduğumuzda a, bye taşınır ve b, anın sahibi olur. Daha sonra c'yi oluştururken a'yı tekrar kullanmaya çalıştığımızda, a taşındığı için buna izin verilmez.

not

Cons tanımını referans tutacak şekilde değiştirebiliriz, ancak o zaman ömür parametrelerini belirtmemiz gerekecektir. Ömür parametrelerini belirterek, listedeki her öğenin tüm listenin süresi boyunca en azından yaşayacağını belirtmiş oluruz. Bu durum, Liste 15-17'deki öğeler ve listeler için geçerlidir, ancak her senaryoda değil.

Bunun yerine, List tanımımızı Box yerine Rc kullanacak şekilde değiştireceğiz; bu, Liste 15-18'de gösterilmiştir. Her Cons varyantı şimdi bir değer ve bir List'e işaret eden bir Rc tutacaktır. b oluşturduğumuzda, a'nın sahipliğini almak yerine a'nın tutmakta olduğu Rc'i klonlayacağız ve böylece referans sayısını birden ikiye artırarak a ve b'nin o Rc içerisindeki verinin sahipliğini paylaşmasını sağlayacağız. c'yi oluştururken de a'yı klonlayacağız, referans sayısını ikiye üçe çıkaracağız. Rc::clone çağrısı her yapıldığında, Rc içindeki veriye olan referans sayısı artacak ve veri, buna sıfır referans olduğunda temizlenecek.

kullananList` tanımı">

{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-18/src/main.rs}}

Rc'yi kapsam içine almak için bir use ifadesi eklememiz gerekiyor çünkü bu ön tanımda yok. main'de, 5 ve 10 tutan listeyi oluşturup bunu yeni bir Rc olarak aya kaydediyoruz. Sonra b ve c oluşturduğumuzda, Rc'teki a referansını argüman olarak geçirerek Rc::clone fonksiyonunu çağırıyoruz.

bilgi

Rc::clone(&a) yerine a.clone() çağrısı yapabilirdik, ama Rust'nın bu durumda Rc::clone kullanma kuralı var. Rc::clone'ın implementasyonu, çoğu türün clone işlemlerinin yaptığı gibi verilerin derin bir kopyasını yapmaz. Rc::clone çağrısı yalnızca referans sayısını artırır ki bu da uzun sürmez.

Verilerin derin kopyaları çok zaman alabilir. Rc::clone'ı referans sayımı için kullanarak, derin kopya türündeki klonlar ile referans sayısını artıran klonlar arasında görsel ayırt etme yapabiliriz. Kodda performans problemleri ararken yalnızca derin kopya klonları dikkate almalı ve Rc::clone çağrılarını göz ardı edebiliriz.

Rc Klonlama, Referans Sayısını Artırır

Şimdi, Liste 15-18'deki çalışma örneğimizi değiştirip, a'daki Rc için referans sayılarının değişimini görebileceğimiz gibi değiştireceğiz.

Liste 15-19'da, main'i c listesi etrafında bir iç kapsam ekleyerek değiştireceğiz; bu şekilde c kapsamdan çıktığında referans sayısının değişimini görebiliriz.

{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-19/src/main.rs:here}}

Programda referans sayısının değiştiği her noktada, Rc::strong_count fonksiyonunu çağırarak elde ettiğimiz referans sayısını yazdırıyoruz. Bu fonksiyon, strong_count olarak adlandırılmıştır çünkü Rc türü aynı zamanda bir weak_count değerine sahiptir; weak_count'ın ne için kullanıldığını, “Referans Döngülerini Önleme: Rc'yi Weak'ye Dönüştürme” bölümünde göreceğiz.

Bu kod aşağıdaki çıktıyı yazdırır:

{{#include ../listings/ch15-smart-pointers/listing-15-19/output.txt}}

a'daki Rc başlangıçta 1 referans sayısına sahiptir; her clone çağrıldığında bu sayı 1 artar. c kapsamdan çıktığında, sayı 1 azalır. Referans sayısını artırmak için Rc::clone çağrısının aksine, referans sayısını azaltmak için bir fonksiyon çağırmamıza gerek yoktur: Rc değeri kapsamdan çıktığında, Drop trait'inin implementasyonu referans sayısını otomatik olarak azaltır.

tehlike

Bu örnekte göremediğimiz şey, b ve ardından a main'in sonunda kapsamdan çıktığında, sayının 0'a düşmesi ve Rc'nin tamamen temizlenmesidir. Rc kullanmak, tek bir değerin birden fazla sahibi olmasına olanak tanır, ve referans sayısı, sahibi olan herhangi bir nesne hala var olduğu sürece değerin geçerli kalmasını sağlar.

Değiştirilemez referanslar aracılığıyla, Rc veriyi programınızın birden fazla parçası arasında yalnızca okuma amacıyla paylaşmanıza olanak tanır. Eğer Rc aynı yere birden fazla değiştirilebilir referans sahibi olmanıza izin verseydi, Bölüm 4'te ele alınan borçlanma kurallarından birinin ihlaline neden olabilirdi: aynı yere birden fazla değiştirilebilir borç almak veri yarışmalarına ve tutarsızlıklara neden olabilir. Ancak verileri değiştirebilmek çok faydalıdır! Sonraki bölümde, bu değişmezlik kısıtlamasıyla çalışmak için Rc ile birlikte kullanabileceğiniz iç değiştirilebilirlik kalıbını ve RefCell türünü tartışacağız.