Skip to content

Commit 3b317ba

Browse files
committed
Updated the topic
1 parent 4740580 commit 3b317ba

File tree

4 files changed

+208
-1
lines changed

4 files changed

+208
-1
lines changed

Lesson_13/MacroTestResult.png

29.2 KB
Loading

Lesson_13/ProceduralMacros.png

22.6 KB
Loading

Lesson_13/README.md

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,139 @@ mod tests {
142142
Örneğin Product, Customer, Order, Category ve benzeri entity nesnelerinin yer aldığı bir senaryoda her birisi için ayrı
143143
ayrı struct geliştirmek yerine bir makro ile kod tekrarlarının önüne geçebilir, veri yapılarını basitçe
144144
tanımlayabiliriz. crud isimli makro argüman olarak gelen identifier ve type bilgilerini kullanarak struct'ın temel
145-
halini inşa eder ve aynı zamanda new metodunu otomatik olarak implemente eder.
145+
halini inşa eder ve aynı zamanda new metodunu otomatik olarak implemente eder.
146+
147+
Sıradaki örnek makro bir kod bloğunun çalışma süresini ölçümlemekte kullanılır.
148+
149+
```rust
150+
macro_rules! wt {
151+
($block:block) => {{
152+
let start = std::time::Instant::now();
153+
let result = $block;
154+
let duration = start.elapsed();
155+
println!("Total execution time: {:?}", duration);
156+
result
157+
}};
158+
}
159+
160+
#[cfg(test)]
161+
mod tests {
162+
use super::*;
163+
164+
#[test]
165+
fn wt_test() {
166+
let sum = wt!({
167+
let mut total = 0;
168+
for i in 1..100 {
169+
total += i;
170+
}
171+
total
172+
});
173+
assert_eq!(sum, 4950);
174+
}
175+
}
176+
```
177+
178+
Örnekteki makro $block ifadesi ile aslında bir kod bloğunu ele alır. Bu bloğun öncesine bir sayaç yerleştirir ve son
179+
olarak da çıktıyı terminal ekranına basar. println! kullanımı demo ve öğrenim senaryoları için yeterlidir ancak makronun
180+
bir kütüphane üzerinden kullanıma açışması söz konusu olacaksa terminal bağımsız çalışan kısacası stdout üzerinden çıktı
181+
veren bir hale getirilmesi daha doğru olur. Bu, makronun biraz daha farklı yazılmasını gerektirebilir. Aşağıdaki kod
182+
parçasında bu durum ele alınmaktadır.
183+
184+
```rust
185+
macro_rules! wt_with {
186+
($writer:expr, $block:block) => {{
187+
use std::io::Write;
188+
let start = std::time::Instant::now();
189+
let result = $block;
190+
let duration = start.elapsed();
191+
writeln!($writer, "Total execution time: {:?}", duration).unwrap();
192+
result
193+
}};
194+
}
195+
196+
#[cfg(test)]
197+
mod tests {
198+
use super::*;
199+
200+
#[test]
201+
fn wt_with_test() {
202+
let sum = wt_with!(std::io::stdout(), {
203+
let mut total = 0;
204+
for i in 1..100 {
205+
total += i;
206+
}
207+
total
208+
});
209+
assert_eq!(sum, 4950);
210+
}
211+
}
212+
```
213+
214+
Makroya parametre atandığına dikkat edilmelidir. Bu bir nevi writeln! çağrısının hangi ortama yapılacağının
215+
soyutlanmasıdır. Test metodundaki gibi stdout verilmesi, bilginin terminaldeki test çıktısına yansıtılmasını sağlar.
216+
217+
![Macro Test Result](MacroTestResult.png)
218+
219+
Dolayısıyla çıktının writeln! makrosunu kullanabilen bir logger'a, network stream'a veya bir veritabanına aktarılması da
220+
mümkündür. _(Bu durum C#, Java gibi dillerdeki bileşen bağımlılıklarının metotlar üzerinden enjekte edilerek
221+
kullanılmasına da benzetilebilir. Daha detaylı bilgi için Dependency Injection konusuna bakılabilir)_
222+
223+
Devam eden örnekte ise kod bloğu içerisinde gönderilen bir veri yapısının XML çıktısını hazırlayan kodların yazıldığı
224+
bir makro söz konusudur.
225+
226+
```rust
227+
macro_rules! wt_with {
228+
($writer:expr, $block:block) => {{
229+
use std::io::Write;
230+
let start = std::time::Instant::now();
231+
let result = $block;
232+
let duration = start.elapsed();
233+
writeln!($writer, "Total execution time: {:?}", duration).unwrap();
234+
result
235+
}};
236+
}
237+
238+
#[cfg(test)]
239+
mod tests {
240+
use super::*;
241+
242+
#[test]
243+
fn xml_test() {
244+
let data = xml! {
245+
game {
246+
id:1,
247+
title:"Pacman 1983",
248+
rate:9.6
249+
}
250+
};
251+
assert_eq!(
252+
data,
253+
"<game id=\"1\" title=\"Pacman 1983\" rate=\"9.6\" />".to_string()
254+
);
255+
}
256+
}
257+
```
258+
259+
## Procedural Macros
260+
261+
Procedural makrolar bir Rust kodundan yararlanarak başka bir rust kodu üretilmesinde sıklıkla kullanılır. TokenStream
262+
girdileri ile çalışır. Temelde üç türü vardır. Derive direktifi ile kullanılanlar, attribute olarak kullanılanlar ve
263+
fonksiyon stilinde kullanılanlar.
264+
265+
![Procedural Macro Types](ProceduralMacros.png)
266+
267+
Declarative makrolar ile aralarında bazı farklılıklar da vardır. Bunlar aşağıdaki tabloda özetlenmiştir.
268+
269+
| Özellik | Declarative Macros (`macro_rules!`) | Procedural Macros |
270+
|--------------------------|--------------------------------------------------------|---------------------------------------------------------------|
271+
| **Kullanım Zorluğu** | Basit, hızlı öğrenilir | Daha karmaşıktır ve öğrenmesi zaman alır |
272+
| **Kod Genişletme** | Pattern matching ile belirgin genişletme sağlar | Kod analizi ile daha karmaşık işlemler yapılabilir |
273+
| **Hata Mesajları** | Derleyici hatalarının anlaşılması zor olabilir | Daha karmaşık hatalar üretir |
274+
| **Performans** | Çok hızlıdır, compile-time'da minimal etkisi vardır | Derleme süresinin artmasına neden olabilir |
275+
| **Karmaşıklık Yönetimi** | Büyük ve karmaşık işleri yönetmek zordur | Büyük projelerde karmaşıklığın daha iyi yönetilmesini sağlar |
276+
| **Kapsam** | Kod tekrarını azaltma veya basit DSL'ler için idealdir | Gelişmiş DSL'ler, derive ve attribute işlevleri için idealdir |
277+
278+
Procedural Macro'lar, proc-macro crate olarak adlandırılan ayrı bir kütüphanede yazılırlar. Rust söz dizimi üzerinde
279+
TokenStream kullanılarak işlem yapılması bazı durumlarda zorlayıcı olabilir. syn ve quote gibi küfeler genellikle işi
280+
kolaylaştıran enstrümanlar içerirler.

Lesson_13/src/samples.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,81 @@ macro_rules! crud {
3535

3636
crud!(Product, id: i32,title: String,list_price:f32,category: String);
3737

38+
#[allow(unused_macros)]
39+
macro_rules! wt {
40+
($block:block) => {{
41+
let start = std::time::Instant::now();
42+
let result = $block;
43+
let duration = start.elapsed();
44+
println!("Total execution time: {:?}", duration);
45+
result
46+
}};
47+
}
48+
49+
#[allow(unused_macros)]
50+
macro_rules! xml {
51+
($tag:ident { $($key:ident : $value:expr),* }) => {
52+
format!(
53+
"<{tag} {attributes} />",
54+
tag = stringify!($tag),
55+
attributes = vec![$(format!("{}=\"{}\"", stringify!($key), $value)),*].join(" ")
56+
)
57+
};
58+
}
59+
60+
#[allow(unused_macros)]
61+
macro_rules! wt_with {
62+
($writer:expr, $block:block) => {{
63+
use std::io::Write;
64+
let start = std::time::Instant::now();
65+
let result = $block;
66+
let duration = start.elapsed();
67+
writeln!($writer, "Total execution time: {:?}", duration).unwrap();
68+
result
69+
}};
70+
}
71+
3872
#[cfg(test)]
3973
mod tests {
4074
use super::*;
75+
76+
#[test]
77+
fn xml_test() {
78+
let data = xml! {
79+
game {
80+
id:1,
81+
title:"Pacman 1983",
82+
rate:9.6
83+
}
84+
};
85+
assert_eq!(
86+
data,
87+
"<game id=\"1\" title=\"Pacman 1983\" rate=\"9.6\" />".to_string()
88+
);
89+
}
90+
#[test]
91+
fn wt_with_test() {
92+
let sum = wt_with!(std::io::stdout(), {
93+
let mut total = 0;
94+
for i in 1..100 {
95+
total += i;
96+
}
97+
total
98+
});
99+
assert_eq!(sum, 4950);
100+
}
101+
#[test]
102+
fn wt_test() {
103+
let sum = wt!({
104+
let mut total = 0;
105+
for i in 1..100 {
106+
total += i;
107+
}
108+
total
109+
});
110+
assert_eq!(sum, 4950);
111+
}
112+
41113
#[test]
42114
fn test_crud_macro() {
43115
let c64 = Product::new(

0 commit comments

Comments
 (0)