Skip to content

Commit 09061d0

Browse files
committed
Added 'closures' chapter
1 parent c960914 commit 09061d0

File tree

6 files changed

+408
-2
lines changed

6 files changed

+408
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[workspace]
2-
members = ["Lesson_00", "Lesson_01", "Lesson_02", "Lesson_03", "Lesson_04", "Lesson_05", "Lesson_06", "Lesson_07", "Lesson_08", "collector", "drone-lab", "fops", "mocking", "sysco", "sysco2"]
2+
members = ["Lesson_00", "Lesson_01", "Lesson_02", "Lesson_03", "Lesson_04", "Lesson_05", "Lesson_06", "Lesson_07", "Lesson_08", "Lesson_09", "collector", "drone-lab", "fops", "mocking", "sysco", "sysco2"]
33

44
resolver = "2"

Lesson_09/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "Lesson_09"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]

Lesson_09/README.md

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Ders 09: Closures
2+
3+
Closure terimi çoğu zaman anonim fonksiyon olarak da ifade edilir. Özellikle fonksiyonel dillerde yaygın olarak kullanılan closure'lar, bir değişkene atanabilir ve bu sayede fonksiyonlara parametre olarak kod bloklarının taşınması sağlanabilir. Aşağıdaki basit kod parçasında en temek haliyet bir closure tanımı yer almaktadır.
4+
5+
```rust
6+
let square = |x| x * x;
7+
println!("{}", square(5));
8+
```
9+
10+
Örnekte tanımlanan square isimli değişken x değerlerinin çarpımını hesap eden bir kod bloğunu ifade eder. square değişkenini normal bir fonksiyon gibi çağırabiliriz. Rust, built-in olarak üç önemli closure sunar.
11+
12+
- **Fn:** Closure, dışarıdan yakaladığı değişkenleri salt okunur _(read only)_ şekilde kullanır.
13+
- **FnMut:** Closure, dış değişkenleri değiştirerek _(mutable)_ kullanabilir.
14+
- **FnOnce:** Closure, dış değişkenleri sahiplenir (move eder) ve yalnızca bir kez çağrılabilir.
15+
16+
Özellikle nesne toplulukları üzerinden hareket eden iteratif fonksiyonları bu ön tanımlı closure'ları sıklıkla kullanır. Buradaki türler C# tarafından gelenler için delegate türüne benzetilebilir.
17+
18+
## Filtreleme ve Sıralama İşlemleri
19+
20+
Takip eden örnekler Game türünden veriler içeren bir vector ile ilişkilidir. Örnek oyun bilgileri için repository.rs dosyasına bakılabilir. Çok basit bir örnekle başlayalım. Oyunları yıllara göre sıralamak istediğimiz düşünelim. Normalde vector türleri belli bir key değerine göre sıralama işlemi için sort_by_key isimli metodu sağlar. Bu metod FnMut(&T) -> K türünden bir parametre kullanır. Bir başka deyişle sıralama için kullanılacak anahtar alanı ele alması gerekir. Buna göre aşağıdaki gibi bir örnek yazılabilir.
21+
22+
```rust
23+
mod repository;
24+
25+
use repository::*;
26+
27+
fn year_sorter(game: &Game) -> u16 {
28+
game.year
29+
}
30+
31+
fn print_games(games: &Vec<Game>) {
32+
for game in games {
33+
println!("{}, {}", game.year, game.title);
34+
}
35+
}
36+
37+
fn main() {
38+
let games = repository::load_games();
39+
40+
let mut games = repository::load_games();
41+
42+
games.sort_by_key(year_sorter);
43+
print_games(&games);
44+
}
45+
```
46+
47+
Diğer yandan aynı işlev year_sorter fonksiyonunu yazmaya gerek kalmadan sort_by metoduna FnMut ile taşınabilecek bir kod bloğu gönderilerek de gerçekleştirilebilir. Hatta farklı sıralama ve filtreleme kritleri de aynı metodoloji ile uygulanabilir. Aşağıdaki örnek kodlarda bu durum ele alınır.
48+
49+
```rust
50+
fn main() {
51+
let games = repository::load_games();
52+
let mut games = repository::load_games();
53+
54+
// Closure ile artan yıl sıralaması
55+
games.sort_by(|g1, g2| g1.year.cmp(&g2.year));
56+
println!("Yıla göre artan sıralama:");
57+
print_games(&games);
58+
59+
// Closure ile azalan yıl sıralaması
60+
games.sort_by(|g1, g2| g2.year.cmp(&g1.year));
61+
println!("\nYıla göre azalan sıralama:");
62+
print_games(&games);
63+
64+
// Popülaritesi 2.0'den yüksek olan oyunlar
65+
let popular_games: Vec<Game> = games.into_iter().filter(|g| g.popularity > 2.0).collect();
66+
println!("\nPopüler oyunlar (popularity > 2.0):");
67+
print_games(&popular_games);
68+
}
69+
```
70+
71+
sort_by ve into_iter çağrısı sonrası erişilen filter metotları parametre olarak closure alır. Buna göre tüm nesen koleksiyonu üzerinde closure ifadesi ile gelen kod bloğu çalıştırılır. Örneğin popülerlik değeri 2.0 üzerinden olanları toplamak için aşağıdaki closure kullanılmıştır.
72+
73+
```text
74+
|g| g.popularity > 2.0
75+
```
76+
77+
## Metot Parametresi Olarak Closure Kullanımı
78+
79+
Pekçok sebepten bir nesne topluluğunun çalışma zamanında neye göre filtreleneceği bilinmez. Programcı söz konusu nesne topluluğu üzerinde işletmek istediği kod bloklarını bir fonksiyon gibi geçebilmelidir. Closure ifadeleri bunu gerçekleştirmek için idealdir. Basit bir oyun sistemi tasarladığımız düşünelim. Aşağıdaki veri modellerini kullanıyoruz.
80+
81+
```rust
82+
#[derive(Debug)]
83+
struct Player {
84+
id: u32,
85+
position: (f32, f32),
86+
velocity: (f32, f32),
87+
score: u32,
88+
}
89+
90+
#[derive(Debug)]
91+
struct GameWorld {
92+
players: Vec<Player>,
93+
}
94+
```
95+
96+
Basitçe oyun sahasındaki oyuncuları tanımlayan ve nesne topluluğu olarak ele alan iki veri yapısı var. Sahadaki tüm oyuncular için farklı işlevleri işletecek farklı sistemler tasarlanabilir. Söz gelimi tüm oyuncların pozisyon bilgilerini değiştirecek tek bir fonksiyon yazılabilir veya oyunculardan belli kriterlere uyanların skorlarında değişiklik yapacak bir sistem fonksiyonu da geliştirilebilir. Burada anahtar nokta sistem fonksiyonlarının işletecekleri kodun ne olacağını bilmemeleridir. Aşağıdaki kod parçasında Fn ve FnMut trait'lerinin örnek kullanımlarını içermektedir.
97+
98+
```rust
99+
fn update_players_system<F>(world: &mut GameWorld, mut f: F)
100+
where
101+
F: Fn(&mut Player),
102+
{
103+
for p in &mut world.players {
104+
f(p);
105+
}
106+
}
107+
108+
fn update_score_system<F>(world: &GameWorld, mut f: F)
109+
where
110+
F: FnMut(&Player),
111+
{
112+
/*
113+
Burada FnMut yerine Fn kullanıp oluşan hata mesajı incelenebilir.
114+
115+
error[E0594]: cannot assign to `total_team_score`, as it is a captured variable in a `Fn` closure
116+
change this to accept `FnMut` instead of `Fn`
117+
*/
118+
for p in &world.players {
119+
f(p);
120+
}
121+
}
122+
123+
pub fn main() {
124+
let mut world = GameWorld {
125+
players: vec![
126+
Player {
127+
id: 1,
128+
position: (0.0, 0.0),
129+
velocity: (2.0, 0.0),
130+
score: 0,
131+
},
132+
Player {
133+
id: 2,
134+
position: (100.0, 0.0),
135+
velocity: (8.0, 0.0),
136+
score: 0,
137+
},
138+
],
139+
};
140+
141+
let apply_gravity = |entity: &mut Player| {
142+
entity.position.0 += entity.velocity.0 * 0.9;
143+
entity.position.1 += entity.velocity.1 * 0.9;
144+
};
145+
146+
println!("Before Update: {:?}", world.players);
147+
update_players_system(&mut world, apply_gravity);
148+
// update_players_system(&mut world, |entity| {
149+
// entity.position.0 += entity.velocity.0 * 0.9;
150+
// entity.position.1 += entity.velocity.1 * 0.9;
151+
// });
152+
println!("After Update: {:?}", world.players);
153+
154+
// FnMut kullanımı ile ilgili bir örnek
155+
let mut total_team_score = 0;
156+
157+
println!("Total score before update: {}", total_team_score);
158+
update_players_system(&mut world, |p| p.score += 2);
159+
update_score_system(&world, |p: &Player| {
160+
total_team_score += p.score;
161+
});
162+
println!("Total score after update: {}", total_team_score);
163+
}
164+
```
165+
166+
Dikkat edileceği fonksiyonlar parametre olarak gelen kod bloklarının world nesnesi üzerinden ulaşılan tüm player değişkenleri için icra eder. update_player_system fonksiyonunda kullanılan f değişkeni generic bir tür olarak belirtilmiştir ve Fn trait'ini uygulaması beklenmektedir. Kısaca f yerine Fn trait'ine uygun bir closure ifadesi gelebilir. Örneğin sistem fonksiyonuna apply_gravity değişkeni ile tanımlı fonksiyonu atanabilir ya da closure ifadesi ile doğrudan bir blok gönderilebilir. update_score_system fonksiyonunda FnMut trait'ini uygulayan bir closure ifadesi beklenir. Bu örnek main fonksiyonunda yer alan total_team_score değişkeni üzerinde değişiklik yapar. FnMut olarak tanımlanmasının bir sebebi de bulunduğu scope dışındaki bir değişken üzerinde değişiklik yapacak olmasıdır.
167+
168+
## FnOnce Senaryosu
169+
170+
FnOnce, kullandığı değerleri sahiplenen ve bir sefer çalıştırılması istenen senaryolar için idealdir. Daha çok thread'lerin kullanıldığı durumlarda ele alınabilir. Aşağıdaki örnek kod parçasını bu anlamda ele alabiliriz.
171+
172+
```rust
173+
fn main()
174+
{
175+
// FnOnce Örneği
176+
let message = Some(String::from("You have unlocked a new level!"));
177+
let show_message = || {
178+
if let Some(msg) = message {
179+
println!("{}", msg);
180+
} else {
181+
println!("Message already shown.");
182+
}
183+
};
184+
185+
show_message();
186+
// show_message(); // Burada 'value used here after move' hatası oluşur
187+
/*
188+
Henüz erken olsa da thread açmak FnOnce kullanımı için iyi bir örnek olabilir.
189+
thread::spawn yeni bir thread başlatırken FnOnce türünden bir closure alır. Dışarıdan
190+
değerler closure içerisine taşınır ve thread sonlanana kadar closure sahip olduğu tüm
191+
değerleri tüketir. Bu tek sefer çalıştırılması gereken bir closure olarak düşünülebilir.
192+
*/
193+
let message = String::from("Inside a thread!");
194+
let handle = thread::spawn(move || {
195+
println!("{}", message);
196+
});
197+
// println!("{}", message); // value borrowed here after move
198+
handle.join().unwrap();
199+
}
200+
```

Lesson_09/src/main.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
mod repository;
2+
3+
use repository::*;
4+
5+
fn year_sorter(game: &Game) -> u16 {
6+
game.year
7+
}
8+
9+
fn print_games(games: &Vec<Game>) {
10+
for game in games {
11+
println!("{}, {}", game.year, game.title);
12+
}
13+
}
14+
15+
fn main() {
16+
let games = repository::load_games();
17+
18+
let mut games = repository::load_games();
19+
20+
// Yıla göre sıralama klasik fonksiyon ile
21+
// games.sort_by_key(year_sorter);
22+
23+
// Closure ile artan yıl sıralaması
24+
games.sort_by(|g1, g2| g1.year.cmp(&g2.year));
25+
println!("Yıla göre artan sıralama:");
26+
print_games(&games);
27+
28+
// Closure ile azalan yıl sıralaması
29+
games.sort_by(|g1, g2| g2.year.cmp(&g1.year));
30+
println!("\nYıla göre azalan sıralama:");
31+
print_games(&games);
32+
33+
// Popülaritesi 2.0'den yüksek olan oyunlar
34+
let popular_games: Vec<Game> = games.into_iter().filter(|g| g.popularity > 2.0).collect();
35+
println!("\nPopüler oyunlar (popularity > 2.0):");
36+
print_games(&popular_games);
37+
38+
// Fn, FnMut Kullanımları
39+
let mut world = GameWorld {
40+
players: vec![
41+
Player {
42+
id: 1,
43+
position: (0.0, 0.0),
44+
velocity: (2.0, 0.0),
45+
score: 0,
46+
},
47+
Player {
48+
id: 2,
49+
position: (100.0, 0.0),
50+
velocity: (8.0, 0.0),
51+
score: 0,
52+
},
53+
],
54+
};
55+
56+
let apply_gravity = |entity: &mut Player| {
57+
entity.position.0 += entity.velocity.0 * 0.9;
58+
entity.position.1 += entity.velocity.1 * 0.9;
59+
};
60+
61+
println!("Before Update: {:?}", world.players);
62+
// update_players_system(&mut world, apply_gravity);
63+
update_players_system(&mut world, |entity| {
64+
entity.position.0 += entity.velocity.0 * 0.9;
65+
entity.position.1 += entity.velocity.1 * 0.9;
66+
});
67+
println!("After Update: {:?}", world.players);
68+
69+
// FnMut kullanımı ile ilgili bir örnek
70+
let mut total_team_score = 0;
71+
72+
println!("Total score before update: {}", total_team_score);
73+
update_players_system(&mut world, |p| p.score += 2);
74+
update_score_system(&world, |p: &Player| {
75+
total_team_score += p.score;
76+
});
77+
println!("Total score after update: {}", total_team_score);
78+
}
79+
80+
#[derive(Debug)]
81+
struct Player {
82+
id: u32,
83+
position: (f32, f32),
84+
velocity: (f32, f32),
85+
score: u32,
86+
}
87+
88+
#[derive(Debug)]
89+
struct GameWorld {
90+
players: Vec<Player>,
91+
}
92+
93+
fn update_players_system<F>(world: &mut GameWorld, mut f: F)
94+
where
95+
F: Fn(&mut Player),
96+
{
97+
for p in &mut world.players {
98+
f(p);
99+
}
100+
}
101+
102+
fn update_score_system<F>(world: &GameWorld, mut f: F)
103+
where
104+
F: FnMut(&Player),
105+
{
106+
for p in &world.players {
107+
f(p);
108+
}
109+
}

0 commit comments

Comments
 (0)