@@ -16,13 +16,6 @@ duralım. Rust dilinde birçok smart pointer vardır. Box, RefCell, Rc, Arc gibi
16
16
- ** Deref** ve ** Drop** trait’lerini implemente eden struct türleri olarak tasarlanabilirler _ (Yani kendi Smart
17
17
Pointer modellerimizi tasarlayabiliriz)_
18
18
19
- ## Hangisi ne zaman?
20
-
21
- ** Box** ve ** RefCell** birden fazla sahipliği tek bir veri üzerinde sağlarken, Rc aynı veri üzerinden birden fazla
22
- sahiplik sunar. Box immutable veya mutable ödünç alma _ (borrowing)_ için derleme zamanında kontrol sağlar. Rc sadece
23
- immutable borrowing için derleme zamanında kontrol sağlar.RefCell immutable veya mutable ödünç alma için runtime'da
24
- kontrol sağlar.
25
-
26
19
## Boxing
27
20
28
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
@@ -122,4 +115,227 @@ pub fn recursive_sample() {
122
115
}
123
116
```
124
117
125
- // DEVAM EDECEK
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.
120
+
121
+ # Reference Counting
122
+
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.
127
+
128
+ ``` rust
129
+ use std :: rc :: Rc ;
130
+
131
+ fn main () {
132
+ hello_rc ()
133
+ }
134
+
135
+ pub fn hello_rc () {
136
+ let p1 = Rc :: new (String :: from (" Some values on the heap" ));
137
+ let p2 = p1 . clone ();
138
+ let p3 = p2 . clone ();
139
+
140
+ println! (" p1={:?}" , p1 );
141
+ println! (" p2={:?}" , p2 );
142
+ println! (" p3={:?}" , p3 );
143
+ }
144
+ ```
145
+
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.
148
+
149
+ ``` rust
150
+ fn main () {
151
+ run_rc_with_error ()
152
+ }
153
+
154
+ #[derive(Debug )]
155
+ struct Player {
156
+ id : u32 ,
157
+ name : String ,
158
+ friends : Vec <Player >,
159
+ }
160
+
161
+ impl Player {
162
+ fn new (id : u32 , name : & str ) -> Self {
163
+ Player {
164
+ id ,
165
+ name : name . to_string (),
166
+ friends : Vec :: new (),
167
+ }
168
+ }
169
+
170
+ fn add_friend (& mut self , friend : Player ) {
171
+ self . friends. push (friend );
172
+ }
173
+
174
+ fn print (& self ) {
175
+ println! (" {}'s friends:" , self . name);
176
+ for friend in & self . friends {
177
+ println! (" {} (ID: {})" , friend . name, friend . id);
178
+ }
179
+ }
180
+ }
181
+
182
+ pub fn run_rc_with_error () {
183
+ let mut steve = Player :: new (1 , " Stivi Vondır" );
184
+ let lord = Player :: new (2 , " Lord veyda" );
185
+ let anakin = Player :: new (3 , " Anakin" );
186
+
187
+ steve . add_friend (lord ); // lord' un sahipliği add_friend sonrası steve' e taşındı
188
+ steve . add_friend (anakin );
189
+
190
+ steve . print ();
191
+
192
+ println! (" Lord veyda's ID: {}" , lord . id); // Value Moved Here
193
+ }
194
+ ```
195
+
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.
203
+
204
+ ``` rust
205
+ #[derive(Debug )]
206
+ struct Player {
207
+ id : u32 ,
208
+ name : String ,
209
+ friends : Vec <Rc <Player >>,
210
+ }
211
+
212
+ impl Player {
213
+ fn new (id : u32 , name : & str ) -> Rc <Self > {
214
+ Rc :: new (Player {
215
+ id ,
216
+ name : name . to_string (),
217
+ friends : Vec :: new (),
218
+ })
219
+ }
220
+
221
+ fn add_friend (self : & Rc <Self >, friend : Rc <Player >) {
222
+ self . friends. push (friend );
223
+ }
224
+
225
+ fn print (& self ) {
226
+ println! (" {}'s friends:" , self . name);
227
+ for friend in self . friends. iter () {
228
+ println! (" {} (ID: {})" , friend . name, friend . id);
229
+ }
230
+ }
231
+ }
232
+
233
+ pub fn run_rc_with_error_2 () {
234
+ let steve = Player :: new (1 , " Stivi Vondır" );
235
+ let lord = Player :: new (2 , " Lord veyda" );
236
+ let anakin = Player :: new (3 , " Anakin" );
237
+
238
+ steve . add_friend (Rc :: clone (& lord ));
239
+ steve . add_friend (Rc :: clone (& anakin ));
240
+
241
+ steve . print ();
242
+
243
+ println! (" Lord Veyda's ID: {}" , lord . id);
244
+ }
245
+ ```
246
+
247
+ Bu sefer add_friend metodu içerisindeki self.friends.push metodunda bir hata alınır.
248
+
249
+ ``` text
250
+ cannot borrow data in an `Rc` as mutable [E0596]
251
+ cannot borrow as mutable
252
+ Help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Player>`
253
+ ```
254
+
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.
261
+
262
+ ``` rust
263
+ use std :: cell :: RefCell ;
264
+ use std :: rc :: Rc ;
265
+
266
+ fn main () {
267
+ run_rc ()
268
+ }
269
+
270
+ #[derive(Debug )]
271
+ struct Player {
272
+ id : u32 ,
273
+ name : String ,
274
+ friends : RefCell <Vec <Rc <Player >>>,
275
+ }
276
+
277
+ impl Player {
278
+ fn new (id : u32 , name : & str ) -> Rc <Self > {
279
+ Rc :: new (Player {
280
+ id ,
281
+ name : name . to_string (),
282
+ friends : RefCell :: new (Vec :: new ()),
283
+ })
284
+ }
285
+
286
+ fn add_friend (self : & Rc <Self >, friend : Rc <Player >) {
287
+ self . friends. borrow_mut (). push (friend );
288
+ }
289
+
290
+ fn print (& self ) {
291
+ println! (" {}'s friends:" , self . name);
292
+ for friend in self . friends. borrow (). iter () {
293
+ println! (" {} (ID: {})" , friend . name, friend . id);
294
+ }
295
+ }
296
+ }
297
+
298
+ pub fn run_rc () {
299
+ let steve = Player :: new (1 , " Stivi Vondır" );
300
+ let lord = Player :: new (2 , " Lord veyda" );
301
+ let anakin = Player :: new (3 , " Anakin" );
302
+
303
+ steve . add_friend (Rc :: clone (& lord ));
304
+ steve . add_friend (Rc :: clone (& anakin ));
305
+
306
+ steve . print ();
307
+
308
+ println! (" Lord Veyda's ID: {}" , lord . id);
309
+ }
310
+ ```
311
+
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.
317
+
318
+ Bu senaryoya göre farklı kullanım şekilleri de söz konusu olabilir.
319
+
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 >>>
323
+ daha iyi bir çözüm olabilir.
324
+
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)_
327
+
328
+ Buraya kadar gördüğümüz Smart Pointer türlerini aşağıdaki grafikle özetleyebiliriz.
329
+
330
+ ![ Smart Pointers.png] ( smrt_ptrs.png )
331
+
332
+ ## Atomic Reference Counting
333
+
334
+ // Thread'lerin işlendiği bölümde ele alınacaktır
335
+
336
+ ## Hangisi ne zaman?
337
+
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.
0 commit comments