11# Ders 10: Smart Pointers
22
33Verinin bellekteki temsili sırasında şartlara göre farklı senaryolar için farklı enstrümanlar kullanılabilir. Bu nedenle
4- ** Smart Pointer** kullanımını bilmek önemlidir. Smart Pointer türlerine geçmeden önce bazı temek kavramlar üzerinde
5- duralım. Rust dilinde birçok smart pointer vardır. Box, RefCell, Rc, Arc gibi
6-
7- - Pointer
4+ ** Smart Pointer** türlerini bilmek önemlidir. Rust dilinde birçok smart pointer bulunur. Box, RefCell, Rc, Arc gibi Çoğu
5+ durumda derleme zamanında verinin nerede tutulacağı ve ne kadar yer kaplayacağına dair Rust'ın öngörüleri vardır. Bazı
6+ hallerde Stack odaklı verilerin kasıtlı olarak heap'e alınması istenebilir ya da heap'te yer alan verinin farklı
7+ thread'ler tarafından güvenli bir şekilde kullanılabilmesi ve üstelik mutable olarak ele alınması da gerekebilir. Aynı
8+ veriye birden fazla sahipliğin gelebilme ihtimali ve ek olarak mutable erişim ihtiyaçları akıllı işaretçiler tarafından
9+ daha kolay yönetilebilir.
10+
11+ - ** Pointer**
812 - Bellek üzerindeki bir veri içeriğini işaret eden adres bilgisini taşıyan değişken olarak düşünülebilir.
9- - Farkında olmadan şu ana kadar kullandığımız bir pointer vardır (&) ve datayı referans etmekte kullanılır.
10- - Smart Pointer
11- - Pointer adreslerine ek metadata bilgileri veya kabiliyetler ekler. Rust diline özel bir kavram değildir
12- esasında C++ orijinlidir.
13- - Referanslar veriyi işaret ederken Smart Pointer’ lar genellikle sahipliğini de alır. String ve Vec< T >
14- türleri smart pointer olarak da geçerler, zira belli bir bellek adresindeki verinin sahipliğini alırlar ve onu
15- manipüle etmemize izin verirler.
16- - ** Deref** ve ** Drop** trait’lerini implemente eden struct türleri olarak tasarlanabilirler _ (Yani kendi Smart
13+ - Farkında olmadan şu ana kadar kullandığımız bir pointer vardır ** (&)** ve veriyi referans etmekte kullanılır.
14+ - ** Smart Pointer**
15+ - ** Pointer** adreslerine ek metadata bilgileri veya kabiliyetler ekler. Rust diline özel bir kavram değildir
16+ ve ** C++** orijinlidir.
17+ - Referanslar veriyi işaret ederken Smart Pointer’ lar genellikle sahipliği _ (Ownership) _ de alır. ** String** ve
18+ ** Vec< T > ** türleri smart pointer olarak da geçerler, zira belli bir bellek adresindeki verinin sahipliğini
19+ alırlar ve onu değiştirmemize izin verirler.
20+ - ** Deref** ve ** Drop** trait’lerini implemente eden veri yapıları olarak da tasarlanabilirler _ (Yani kendi Smart
1721 Pointer modellerimizi tasarlayabiliriz)_
1822
1923## Boxing
2024
21- Bir veriyi Stack yerine Heap üzerinde konuşlandırmanın en basit hali Box enstrümanını kullanmaktır. Aşağıda bu kullanıma
22- ait basit bir fonksiyon yer almaktadır.
25+ Bir veriyi ** Stack** yerine ** Heap** üzerinde konuşlandırmanın en basit hali generic ** Box** türevini kullanmaktır.
26+ Aşağıda bu kullanıma ait basit bir fonksiyon yer almaktadır.
2327
2428``` rust
2529pub fn simple_boxing () {
26- let value = 23 ; // Normalde stack' de saklanır
27- let boxed_value = Box :: new (value ); // Şimdi heap'e alındı ama boxed_value hala stack'te zira adres göstermekte
30+ let value = 23 ;
31+ let boxed_value = Box :: new (value );
2832 println! (" Boxed value is {}" , boxed_value );
2933
30- let identity = (" John Smith" , 23 , true ); // tuple veriyi stack'ta saklar
31- let boxed_identity = Box :: new (identity ); // Şimdi heap' te
34+ let identity = (" John Smith" , 23 , true );
35+ let boxed_identity = Box :: new (identity );
3236 println! (" Boxed identity is {:?}" , boxed_identity );
3337}
3438```
3539
36- value isimli değişken sayısal bir değerdir ve normal şartarda Stack'te tutulur. Box ile söz konusu değişken verisi
37- heap'e alınır ve stack'de onu işaret eden bir işaretçi bırakılır. Benzer bir kullanım şeklide Tuple veri türü içinde ele
38- alınmıştır. Box türünün kullanımı için sıkça vurgulanan senaryolardan birisi ağaç boğum modelleridir _ (Tree Nodes)_
39- Aşağıdaki örnek kod parçasını göz önüne alalım.
40+ ** value** isimli değişken sayısal bir değerdir ve normal şartarda ** Stack** 'te tutulur. ** Box** ile söz konusu değişken
41+ verisi heap'e alınır ve stack'de onu işaret eden bir işaretçi bırakılır. Benzer bir kullanım şekli ** Tuple** veri türü
42+ içinde ele alınmıştır.
43+
44+ Box türünün kullanımı için sıkça vurgulanan senaryolardan birisi ağaç boğum modelleridir _ (Tree Nodes)_ Aşağıdaki örnek
45+ kod parçasını göz önüne alalım.
4046
4147``` rust
4248fn main () {
@@ -54,17 +60,18 @@ pub fn recursive_data_model_with_error() {
5460}
5561```
5662
57- Bu kod parçası derlenmeyecektir ve şöyle bir hata mesajı verecektir.
63+ Tree veri yapısı yine kendi türünden ikili dal şeklinde alt boğumlarını tutabilen bir veri yapısıdır. Lakin bu kod
64+ parçası derlenmeyecektir ve şöyle bir hata mesajı alınacaktır.
5865
5966``` text
6067error[E0072]: recursive type `Tree` has infinite size
6168```
6269
63- Buradakine benzer recursive veri modellerinden datanın ne kadar yer kaplayacağı derleme zamanında bilinemez. Senaryoda
64- enum türü kullanıldığı için de stack önceklikli bir yer ayırma durumu söz konusudur. Ne kadar boyut kaplanacağının
65- bilinmemesi taşma hatalarına sebebiyet verebilir. Bir düğüm kendisine referans verdikçe bu sonsuz boyutlamaya doğru
66- gidebilir. Dolayısıyla veriyi Heap üzerinde konuşlandırmak daha mantıklıdır. Benzer bir senaryoyu bu sefer aşağıdaki
67- gibi tasarlayarak devam edelim.
70+ Bu senaryodaki gibi recursive veri modellerinde datanın ne kadar yer kaplayacağı derleme zamanında bilinemez. Senaryoda
71+ ** enum** türü kullanıldığı için de ** stack** önceklikli bir yer ayırmas durumu söz konusudur. Ne kadar boyut
72+ ayrılması gerektiğinin bilinmemesi taşma hatalarına _ (Stack Overflow) _ sebebiyet verebilir. Bir düğüm kendisine referans
73+ verdikçe bu sonsuz boyutlamaya doğru gidebilir. Dolayısıyla veriyi ** Heap** üzerinde konuşlandırmak daha mantıklıdır.
74+ Benzer bir senaryoyu bu sefer aşağıdaki kod parçasında olduğu gibi tasarlayarak devam edelim.
6875
6976``` rust
7077use std :: fmt :: {Display , Formatter };
@@ -115,15 +122,21 @@ pub fn recursive_sample() {
115122}
116123```
117124
118- Server isimli enum türünün Node alanı içerisinde Box edilen kendi türleri tutulmakta. Burada Box işlemi söz konusu
119- olduğu için bağlı liste Heap üzerinde konuşlandırılacak.
125+ Server isimli ** enum** türü, ** Node** alanı içerisinde ** Box** edilen kendi türlerini tutulmakta. Şirket sunucuları
126+ arasındaki master-child ilişkileri tutmak için kullanılabilecek ilkel bir veri yapısı söz konusu. Server veri
127+ yapısındaki node'lar Box edilerek tutulduğundan Heap alanında konuşlandırılacaklar. Dolayısıyla az önce karşılaşılan
128+ hata ortadan kalkacak. Zira yine de temkinli olmakta fayda var. Veri modelinin kontrolsüz şekilde büyüme ihtimalini de
129+ göz önüne almak gerekmekte. Bu açıdan bakıldığında yük testler, entegrasyon testleri gibi kontrol mekanizmaları ile kodu
130+ kontrol etmek mühim.
120131
121132# Reference Counting
122133
123- Bilinçli bir şekilde Heap üzerine alınan verilerde birden fazla sahipliğin söz konusu olduğu durumlarda referans
124- değerlerin sayımı tutulur. Eğer paylaşımlı thread'ler söz konusu değilse Rc türü ihtiyacı karşılar. Birden fazla thread
125- aynı veri alanı üzerinde çalışma gerektiği durumlarda ise tip güvenliğini gözeten Atomic Reference Counting yani Arc
126- kullanılır. Aşağıdaki kod parçasında en basit haliyle Rc türünden bir smart pointer kullanımı işlenmektedir.
134+ Bilinçli bir şekilde ** Heap** üzerine alınan verilerde birden fazla sahipliğin söz konusu olduğu durumlarda söz konusu
135+ olabilir. Böyle bir senaryoda çalışma zamanının referansların hangi sırayla bellekten düşürüleceğini bilmesi de gerekir.
136+ Buna özel yazılmış olan smart pointer türleri oluşturulan referanslası sayar ve eğer paylaşımlı thread'ler söz konusu
137+ değilse ** Rc** türü ihtiyacı karşılar. Birden fazla ** thread'in** aynı veri alanı üzerinde çalışması gerektiği
138+ durumlarda ise tip güvenliğini gözeten ** Atomic Reference Counting** yani generic ** Arc** türü kullanılır. Aşağıdaki kod
139+ parçasında en basit haliyle generic ** Rc** türünden bir smart pointer kullanımı işlenmektedir.
127140
128141``` rust
129142use std :: rc :: Rc ;
@@ -143,8 +156,8 @@ pub fn hello_rc() {
143156}
144157```
145158
146- Bu örnekte yer alan p1, p2 ve p3 değişkenleri aynı string veriyi içeren işaretçilerdir. Rc türüne olan ihtiyacı daha iyi
147- anlamak için aşağıdaki basit örneğe bakalım .
159+ Bu örnekte yer alan p1, p2 ve p3 değişkenleri aynı string veriyi içeren işaretçilerdir. Senaryoyu biraz daha
160+ zorlaştıralım .
148161
149162``` rust
150163fn main () {
@@ -193,13 +206,16 @@ pub fn run_rc_with_error() {
193206}
194207```
195208
196- Bir oyuncunun arkadaşlarını da yine kendi türünden Vector olarak tutan Player isimli bir veri yapısı mevcut. add_friend
197- metodu ile bir oyuncuya başka Player örnekleri ekleyebiliyoruz. Player'ın sahip olduğu veri üzerinde değişiklik söz
198- konusu. Temsili run metoduna baktığımızda son satırdaki println! çağrısında value moved here hatası alırız. Bu son
199- derece doğaldır zira steve değişkeni üzerinden yapılan ilk add_friend çağrısı sırasında lord değişkeninin sahipliği de
200- taşınır. Dolayısıyla add_friend sonrası lord değişkenine tekrardan erişilemez. Bu tip bir senaryoyu yönetmek için Rc
201- smart pointer kullanılabilir. Ancak mutable olma zorunluluğuna dikkat etmek gerekir. Bunu daha iyi anlamak için örneği
202- aşağıdaki haliyle değiştirelim.
209+ Bir oyuncunun arkadaşlarını da yine kendi türünden bir ** Vector** üzerinde tutan ** Player** isimli bir veri yapısı
210+ mevcut. ** add_friend** metodu ile bir oyuncuya başka ** Player** örnekleri eklenebilmekte. Player türünden bir değişken
211+ oluşturulduğunda ve add_friends ile kendi türünden bir değişken eklenmek istendiğin işler biraz karışır. Zira Player'ın
212+ sahipliğini aldığı bir vector üzerinde değişiklik söz konusudur. Vector türü referans türevlidir. Temsili run metodu
213+ çağırıldığında son satırda yer alan ** println!** makro çağrısında ** value moved here** hatası alınır. Bu son
214+ derece doğaldır zira ** steve** değişkeni üzerinden yapılan ilk ** add_friend** çağrısı sırasında ** lord** değişkeninin
215+ sahipliği de bu metoda taşınır. Dolayısıyla ** add_friend** sonrası ** lord** değişkenine tekrardan erişilemez. _ (Burada
216+ klonlama gibi bir yöntemi deneyerek sonucun değişip değişmeyeceğini irdelemenizi öneririm_ ) Bu tip bir
217+ senaryoyu yönetmek için ** Rc smart pointer** kullanılabilir. Ancak ** mutable** olma zorunluluğuna dikkat etmek gerekir.
218+ Durumu daha iyi anlamak için örneği aşağıdaki gibi değiştirelim.
203219
204220``` rust
205221#[derive(Debug )]
@@ -244,20 +260,23 @@ pub fn run_rc_with_error_2() {
244260}
245261```
246262
247- Bu sefer add_friend metodu içerisindeki self.friends.push metodunda bir hata alınır.
263+ Player veri yapısındaki friends isimli alanda Rc kullanılmıştır. Bu sayede Player değişkenlerinin referanslarının
264+ sayılması ve dolayısıyla birden fazla sahipliğin mümkün kılınması hedeflenmektedir. Ancak bu kez de ** add_friend**
265+ metodu içerisindeki ** self.friends.push** çağrımı bir hataya sebebiyet verir.
248266
249267``` text
250268cannot borrow data in an `Rc` as mutable [E0596]
251269cannot borrow as mutable
252270Help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Player>`
253271```
254272
255- Player veri yapısı kendi içerisinden kendi türünden bir Vector kullanmaktadır.İlk hata sebebiyle Vec'ün Rc<Player >
256- şeklinde kullanılması tercih edilebilir. Ancak bu özellikle add_friends metodunda vektör içeriğine mutable erişmeyi
257- gerektirir. Bu nedenle vektöre referansının da mutable olarak ele alınabilmesi gerekir. Normalde bir veriye erişen
258- birden fazla sahip varken mutable kullanım derleme hatasına yol açabilir. ** RefCell** smart pointer kullanımı ile bunu
259- çalışma zamanına taşırız. Yani ownership kontrolünü runtime tarafında işletilmesini sağlarız. Dolayısıyla örnek kodları
260- aşağıdaki şekilde değiştirerek ilerleyebiliriz.
273+ Player veri yapısı kendi içinde kendi türünden bir Vector kullanmaktadır. İlk hata sebebiyle Vectörün ** Rc<Player >**
274+ şeklinde kullanılması tercih edilebilir. Ancak ** add_friends** metodunda vektör içeriğine mutable olarak erişebilmemiz
275+ de gerekmektedir ancak Rust sahiplik ilkeleri ve mutable kuralları buna müsaade etmez. Normalde bir veriye
276+ birden fazla immutable erişim söz konusu olabilir. Böyle bir senaryoda tek bir kişiye mutable izni verilir. Ancak
277+ Rust'ın bazı akıllı işaretçileri bu gibi durumları es geçerek kararın çalışma zamanı tarafından verilmesine olanak
278+ sağlar. ** RefCell** smart pointer kullanımı ile bu mümkündür. Yani ownership kontrolünün ** runtime** tarafında
279+ işletilmesi sağlanabilir. Buna göre örnek kodları aşağıdaki şekilde değiştirerek devam edebiliriz.
261280
262281``` rust
263282use std :: cell :: RefCell ;
@@ -309,33 +328,38 @@ pub fn run_rc() {
309328}
310329```
311330
312- İlk dikkat edilmesi gereken nokta Player veri yapısındaki friends alanının türüdür. Player nesneleri için bir referans
313- sayacı kullanılırken değiştirilebilir olmasını sağlama işi RefCell ile çalışma zamanına bırakılmıştır. new metodu
314- içerisinde RefCell nesnesnin nasıl kullanıldığına da dikkat edelim. Ayrıca friends vektörünü üzerinde değişiklik yapmak
315- üzere kullanacaksak aynen add_friend metodunda olduğu gibi borrow_mut fonksiyonu ile mutable olarak ödünç alınmasını
316- sağlamalıyız. Eğer sadee okuma amaçlı kullanacaksak bu durumda da borrow metodunu kullanmalıyız.
331+ İlk dikkat edilmesi gereken nokta ** Player** veri yapısındaki ** friends** alanının türüdür. ** Player** nesneleri için
332+ bir referans sayacı kullanılırken değiştirilebilir olmasını sağlama işi ** RefCell** ile çalışma zamanına bırakılmıştır.
333+ Bir Player değişkenini oluşturmayı kolaylaştıran ** new** metodu _ (C# tarafındakiler için Constructor)_ içerisinde
334+ RefCell nesnesnin nasıl kullanıldığına da dikkat edelim. Ayrıca ** friends** vektörünü üzerinde değişiklik yapmak
335+ üzere kullanacaksak aynen ** add_friend** metodunda olduğu gibi ** borrow_mut** fonksiyonu ile mutable olarak ödünç
336+ alınmasını sağlanmalıdır. Eğer sadece okuma amaçlı bir kullanım söz konusu ise borrow metodu kullanılmalıdır.
317337
318338Bu senaryoya göre farklı kullanım şekilleri de söz konusu olabilir.
319339
320- - Sadece bir vektör üzerinde çalışılacaksa RefCell<Vec<Player >> kullanımı yeterlidir.
321- - Vektörün paylaşımı söz konusu ise Rc<RefCell<Vec<Player >>> daha uygun bir çözüm olabilir.
322- - Hem vektörü hem de içindeki elemanların paylaşışması gerekiyorsa Rc<Vec<RefCell<Player >>>
340+ - Sadece bir vektör üzerinde çalışılacaksa ** RefCell<Vec<Player >>** kullanımı yeterlidir.
341+ - Vektörün paylaşımı söz konusu ise ** Rc<RefCell<Vec<Player >>>** daha uygun bir çözüm olabilir.
342+ - Hem vektörü hem de içindeki elemanların paylaşılması gerekiyorsa ** Rc<Vec<RefCell<Player >>>**
323343 daha iyi bir çözüm olabilir.
324344
325- Şunu da unutmamamak gerekir hem Rc hem de RefCell kullanımının çalışma zamanı maliyetleri daha yüksektir _ (Zira referans
326- sayımı ve mutasyon kontrolleri yapılmaktadır)_
345+ Unutmamamak gerekir ki gerek ** Rc ** gerek ** RefCell** kullanımlarının çalışma zamanı maliyetleri daha yüksektir _ (Zira
346+ referans sayımı ve mutasyon kontrolleri yapılmaktadır)_
327347
328348Buraya kadar gördüğümüz Smart Pointer türlerini aşağıdaki grafikle özetleyebiliriz.
329349
330350![ Smart Pointers.png] ( smrt_ptrs.png )
331351
332352## Atomic Reference Counting
333353
354+ Referans sayımları için Rc kullanımı yeterlidir ancak veriye farklı thread'lerden erişim söz konusu ise generic Arc türü
355+ ve kilitleme _ (locking)_ mekanizmaları ele alınmalıdır.
356+
334357// Thread'lerin işlendiği bölümde ele alınacaktır
335358
336- ## Hangisi ne zaman ?
359+ ## Hangisi Ne Zaman ?
337360
338- ** Box** ve ** RefCell** birden fazla sahipliği tek bir veri üzerinde sağlarken, Rc aynı veri üzerinden birden fazla
339- sahiplik sunar. Box immutable veya mutable ödünç alma _ (borrowing)_ için derleme zamanında kontroller sağlar. Rc sadece
340- immutable borrowing için derleme zamanında kontrol sağlar. RefCell immutable veya mutable ödünç alma için runtime'da
341- kontrol sağlar.
361+ - ** Box** ve ** RefCell** birden fazla sahipliği tek bir veri üzerinde sağlarken, ** Rc** aynı veri üzerinden birden fazla
362+ sahiplik sunar.
363+ - Box ** immutable** veya ** mutable** ödünç alma _ (borrowing)_ için derleme zamanında kontroller sağlar.
364+ - ** Rc** sadece ** immutable borrowing** için derleme zamanında kontrol sağlar.
365+ - ** RefCell** türü ** immutable** veya ** mutable** ödünç alma için runtime'da kontrol sağlar.
0 commit comments