Skip to content

Commit 26bf879

Browse files
committed
Updated smart pointers topic
1 parent c897a73 commit 26bf879

File tree

3 files changed

+262
-48
lines changed

3 files changed

+262
-48
lines changed

Lesson_10/Readme.md

+224-8
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,6 @@ duralım. Rust dilinde birçok smart pointer vardır. Box, RefCell, Rc, Arc gibi
1616
- **Deref** ve **Drop** trait’lerini implemente eden struct türleri olarak tasarlanabilirler _(Yani kendi Smart
1717
Pointer modellerimizi tasarlayabiliriz)_
1818

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-
2619
## Boxing
2720

2821
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() {
122115
}
123116
```
124117

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.

Lesson_10/smrt_ptrs.png

77.6 KB
Loading

Lesson_10/src/main.rs

+38-40
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,47 @@
1+
use std::cell::RefCell;
2+
use std::rc::Rc;
3+
14
fn main() {
2-
recursive_sample()
5+
run_rc()
36
}
4-
use std::fmt::{Display, Formatter};
57

6-
enum Server {
7-
Node(String, Box<Server>, Box<Server>),
8-
Empty,
8+
#[derive(Debug)]
9+
struct Player {
10+
id: u32,
11+
name: String,
12+
friends: RefCell<Vec<Rc<Player>>>,
913
}
10-
impl Display for Server {
11-
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
12-
match self {
13-
Server::Node(name, primary, backup) => {
14-
write!(
15-
f,
16-
"Server: {}\n Primary: {}\n Backup: {}",
17-
name, primary, backup
18-
)
19-
}
20-
Server::Empty => write!(f, "None"),
14+
15+
impl Player {
16+
fn new(id: u32, name: &str) -> Rc<Self> {
17+
Rc::new(Player {
18+
id,
19+
name: name.to_string(),
20+
friends: RefCell::new(Vec::new()),
21+
})
22+
}
23+
24+
fn add_friend(self: &Rc<Self>, friend: Rc<Player>) {
25+
self.friends.borrow_mut().push(friend);
26+
}
27+
28+
fn print(&self) {
29+
println!("{}'s friends:", self.name);
30+
for friend in self.friends.borrow().iter() {
31+
println!(" {} (ID: {})", friend.name, friend.id);
2132
}
2233
}
2334
}
2435

25-
pub fn recursive_sample() {
26-
let backup_server_blue = Server::Node(
27-
String::from("Backup Server Blue"),
28-
Box::new(Server::Empty),
29-
Box::new(Server::Empty),
30-
);
31-
32-
let primary_server_green = Server::Node(
33-
String::from("Primary Server Green"),
34-
Box::new(Server::Empty),
35-
Box::new(backup_server_blue),
36-
);
37-
38-
let root_server = Server::Node(
39-
String::from("Root Server"),
40-
Box::new(primary_server_green),
41-
Box::new(Server::Node(
42-
String::from("Backup Root"),
43-
Box::new(Server::Empty),
44-
Box::new(Server::Empty),
45-
)),
46-
);
47-
48-
println!("{}", root_server);
36+
pub fn run_rc() {
37+
let steve = Player::new(1, "Stivi Vondır");
38+
let lord = Player::new(2, "Lord veyda");
39+
let anakin = Player::new(3, "Anakin");
40+
41+
steve.add_friend(Rc::clone(&lord));
42+
steve.add_friend(Rc::clone(&anakin));
43+
44+
steve.print();
45+
46+
println!("Lord Veyda's ID: {}", lord.id);
4947
}

0 commit comments

Comments
 (0)