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.
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.
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 a
yı 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
, b
ye taşınır ve b
, a
nı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.
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.
kullanan
List` 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 a
ya kaydediyoruz. Sonra b
ve c
oluşturduğumuzda, Rc
'teki a
referansını argüman olarak geçirerek Rc::clone
fonksiyonunu çağırıyoruz.
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.
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.