11# Ders 13: Macros
22
3- Makrolar meta programlamanın temel unsurlarındadır. Meta programlama kod yazan kodlar oluşturulmasını benimseyen bir
4- yaklaşımdır. Makroları kullanarak kod tekrarını azaltabilir ve daha okunabilir kodların oluşturulmasını sağlayabiliriz.
5- Makrolar ile delerme zamanında kaynak kod üzerinde manipülasyon da yapılabilir. Ayrıca struct, enum, fn gibi yapıların
6- derleme zamanında analiz edilip yeni kodların üretilmesini de sağlayabiliriz. Bugüne kadarki örneklerimizde bir çok
7- makro kullandık. println!, write!, vec! gibi sonu ! ile biten enstrümanlar aslında birer makrodur. Makroları Declarative
8- _ (Bildirime dayalı)_ ve Procedural _ (Yönergelere dayalı) _ olmak üzere iki ana kategoriye ayırabiliriz .
3+ Makrolar kod yazan kodlar oluşturulmasını benimseyen bir meta programlama yaklaşımdır. Makroları kullanarak kod
4+ tekrarlarını azaltabilir, daha okunabilir kodlar oluşturulabilir, delerme zamanında kaynak kod üzerinde değişikliler
5+ yapılabilir. Ayrıca ** struct** , ** enum** , ** fn ** gibi yapıların derleme zamanında analiz edilip yeni kodların üretilmesi
6+ de sağlanabilir. Önceki örneklerde bir çok makro kullanılmıştır. ** println! ** , ** write! ** , ** vec! ** gibi sonu ** ! ** ile
7+ biten enstrümanlar aslında birer makrodur. Makrolar Declarative _ (Bildirime dayalı) _ ve Procedural _ (Yönergelere
8+ dayalı)_ olmak üzere iki ana kategoriye ayrılır .
99
1010## Hello Macro _ (Declarative)_
1111
12- Makrolar belli bir fonksiyonelliğin farklı türevlerinin yazmaktan kaçınmamızı da sağlar . Aşağıdaki basit fonksiyonu göz
12+ Makrolar ile belli bir fonksiyonelliğin farklı türevlerinin yazmak zorunda kalmayız . Aşağıdaki basit fonksiyonu göz
1313önüne alalım.
1414
1515``` rust
@@ -32,10 +32,9 @@ mod tests {
3232}
3333```
3434
35- max_of_two fonksiyonu iki integer değerden hangisi büyükse onu geriye döndürmek amacıyla tasarlanmıştır. Ancak n adet
36- sayının birbiriyle karşılaştırılmasını istediğimiz bir durumda ne yaparız? Bu genellikle yeni bir fonksiyon yazılmasını
37- gerektirecektir. Bunun yerine bir makro hazırlayıp, sum_of fonksiyon bloğunun gelen koşullara göre üretilmesini de
38- sağlayabiliriz.
35+ ** max_of_two** fonksiyonu iki tam sayı değerden hangisi büyükse onu geriye döndürmek amacıyla tasarlanmıştır. Ancak n
36+ adet sayının birbiriyle karşılaştırılması istendiğinde fonksiyonun farkı bir versiyonunun yazılması gerekir. Bunun
37+ yerine bir makro hazırlayıp, ** max_of** fonksiyon bloğunun gelen koşullara göre üretilmesini sağlanabilir.
3938
4039``` rust
4140macro_rules! max_of {
@@ -66,21 +65,22 @@ mod tests {
6665}
6766```
6867
69- Declarative yani bildirime dayalı makrolar yazmak için macro_rules isimli başka bir makro kullanılır. Kendi yazdığımız
70- makrolar dahil isimlendirmelerde fonksiyon adı ! işareti ile sonlandırılır. Örnekte bazı özel kalıpları kullanılmıştır.
68+ Declarative yani bildirime dayalı makrolar yazmak için ** macro_rules** isimli başka bir makro kullanılır. Kendi
69+ yazdığımız makrolar dahil isimlendirmelerde fonksiyon adı ** !** işareti ile sonlandırılır. Örnekte bazı özel kalıplar
70+ kullanılmıştır.
7171
72- - ($x: expr ) => { $x } : Tek bir argüman ile çalışılacağını belirtir. expr ile bir expression türü ifade edilir.
72+ - ($x: expr ) => { $x } : Tek bir argüman ile çalışılacağını belirtir. ** expr** ile bir ** expression** türü ifade edilir.
7373 Dolayısıyla makronun uygulandığı yapıda tek bir sayısal ifade varsa doğrudan döndüren bir kod bloğu üretilecektir.
74- - ($x: expr , $y: expr ) => { ... } : Bu kısımda ise iki argümanla eşleşilen durumu ele alır. Burada if kullanılan bir kod
74+ - ($x: expr , $y: expr ) => { ... } : Bu kısımda ise iki argümanla eşleşilen durum ele alınır. ** if ** kullanılan bir kod
7575 bloğunun üretimi sağlanır.
7676- ($x: expr , $($y: expr ),+) => { ... } : Bu kalıpsa iki veya daha fazla argüman için geçerlidir. İkinci söz diziminde yer
77- alan + operaötörü en az bir veya daha fazla anlamındadır . Bu durumla karşılaşılması halinde recursive olarak kendisini
78- çağıran bir fonksiyon kodu üretilecektir .
77+ alan ** + ** operaötörü en az bir veya daha fazla anlamına gelir . Bu durumla karşılaşılması halinde ** recursive** olarak
78+ kendisini çağıran bir fonksiyon kodu üretilir .
7979
8080## MetaSyntactic Variables
8181
82- Makrolarda ifadeleri analiz etmek ve eşleşmeleri yakalamak için token'lar kullanılır. Bunlardan en çok kullanılanlar
83- aşağıdaki tabloda belirtilmektedir .
82+ Makrolarda ifadeleri analiz etmek ve eşleşmeleri yakalamak için ** token** 'lar kullanılır. En sık kullanılan token
83+ değerleri aşağıdaki tabloda özetlenmiştir .
8484
8585| Token | Açıklama | Örnek |
8686| -----------| -----------------------------------------------------------------------| ---------------------------------------------------|
@@ -97,8 +97,8 @@ aşağıdaki tabloda belirtilmektedir.
9797
9898## Örnekler
9999
100- Aşağıdaki kod parçalarında farklı senaryoların ele alındığı procedural makrolar yer almaktadır. İlk örnek bir model
101- nesnesi için gerekli struct'ı kolayca oluşturmak için kullanılır.
100+ Aşağıdaki kod parçalarında farklı senaryoların ele alındığı ** procedural** makro örnekleri yer almaktadır. İlk örnek bir
101+ model nesnesi için gerekli struct'ın kolayca oluşturulması için kullanılır.
102102
103103``` rust
104104macro_rules! crud {
@@ -139,12 +139,13 @@ mod tests {
139139}
140140```
141141
142- Örneğin Product, Customer, Order, Category ve benzeri entity nesnelerinin yer aldığı bir senaryoda her birisi için ayrı
143- ayrı struct geliştirmek yerine bir makro ile kod tekrarlarının önüne geçebilir, veri yapılarını basitçe
144- 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.
142+ Örneğin ** Product** , ** Customer** , ** Order** , ** Category** ve benzeri entity nesnelerinin yer aldığı bir senaryoda her
143+ biri için ayrı ayrı ** struct** yazmak yerine bir makro ile kod tekrarlarının önüne geçebilir. ** crud** isimli makro
144+ argüman olarak gelen ** identifier** ve ** type** bilgilerini kullanarak ** struct** 'ın temel halini inşa eder ve aynı
145+ zamanda ** new** metodunu da otomatik olarak implemente eder. Bu senaryo veri model nesnelerinin farklı kaynaklardan
146+ geldiği _ (örneğin metin tabanlı dosyalar)_ durumlarda kullanışlı olabilir. Bunu araştırıp deneyiniz.
146147
147- Sıradaki örnek makro bir kod bloğunun çalışma süresini ölçümlemekte kullanılır.
148+ Sıradaki makro bir kod bloğunun çalışma süresini ölçümlemek için kullanılır.
148149
149150``` rust
150151macro_rules! wt {
@@ -175,11 +176,11 @@ mod tests {
175176}
176177```
177178
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.
179+ Örnekteki makro ** $block** ifadesi ile aslında bir kod bloğunu ele alır. Bu bloğun öncesine bir sayaç yerleştirir ve son
180+ olarak da çıktıyı terminal ekranına basar. ** println!** kullanımı demo ve öğrenim senaryoları için yeterlidir ancak
181+ makronun bir kütüphane üzerinden kullanıma açışması söz konusu ise terminal bağımsız çalışan kısacası ** stdout**
182+ üzerinden çıktı veren bir hale getirilmesi daha doğrudur . Bu, makronun biraz daha farklı yazılmasını gerektirebilir.
183+ Aşağıdaki kod parçasında bu durum ele alınmaktadır.
183184
184185``` rust
185186macro_rules! wt_with {
@@ -211,17 +212,19 @@ mod tests {
211212}
212213```
213214
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.
215+ Makroya parametre atandığına dikkat edilmelidir. Bu, bir nevi ** writeln!** çağrısının hangi ortama yapılacağının
216+ soyutlanmasıdır. ** Test** metodundaki gibi ** stdout** verilmesi, bilginin terminaldeki test çıktısına yansıtılmasını
217+ sağlar.
216218
217219![ Macro Test Result] ( MacroTestResult.png )
218220
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)_
221+ Dolayısıyla çıktının ** writeln!** makrosunu kullanabilen bir ** logger** nesnesine, ** network** stream'a veya bir
222+ veritabanına aktarılması da mümkündür. _ (Bu durum C#, Java gibi dillerdeki bileşen bağımlılıklarının metotlar üzerinden
223+ enjekte edilerek kullanılmasına da benzetilebilir. Daha detaylı bilgi için ** Dependency Injection** konusuna
224+ bakılabilir)_
222225
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.
226+ 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
227+ yazıldığı bir makro söz konusudur.
225228
226229``` rust
227230macro_rules! wt_with {
@@ -258,13 +261,15 @@ mod tests {
258261
259262## Procedural Macros
260263
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+ ** Procedural** makrolar bir Rust kodundan yararlanarak başka bir rust kodu üretilmesinde sıklıkla kullanılır.
265+ ** TokenStream** girdileri ile çalışır. Bu nedenle dilin genel semantik yapısına ve abstract syntax tree gibi kavramlara
266+ aşina olmakta yarar vardır. Temelde üç tür ** procedural** makro vardır. ** Derive** direktifi ile kullanılanlar,
267+ ** attribute** şeklinde tasarlananlar ve fonksiyon stilinde yazılanlar.
264268
265269![ Procedural Macro Types] ( ProceduralMacros.png )
266270
267- Declarative makrolar ile aralarında bazı farklılıklar da vardır. Bunlar aşağıdaki tabloda özetlenmiştir.
271+ ** Procedural** ve ** Declarative** makrolar arasında bazı temel farklılıklar vardır. Bunlar aşağıdaki tabloda
272+ özetlenmiştir.
268273
269274| Özellik | Declarative Macros (` macro_rules! ` ) | Procedural Macros |
270275| --------------------------| --------------------------------------------------------| ---------------------------------------------------------------|
@@ -275,9 +280,9 @@ Declarative makrolar ile aralarında bazı farklılıklar da vardır. Bunlar aş
275280| ** 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 |
276281| ** Kapsam** | Kod tekrarını azaltma veya basit DSL'ler için idealdir | Gelişmiş DSL'ler, derive ve attribute işlevleri için idealdir |
277282
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.
283+ Procedural makrolar, ** proc-macro** crate olarak adlandırılan ayrı bir kütüphane formatında yazılırlar. Rust söz dizimi
284+ üzerinde ** TokenStream** kullanılarak işlem yapılması bazı durumlarda zorlayıcı olabilir. ** syn** ve ** quote** gibi
285+ küfeler genellikle işi kolaylaştıran enstrümanlar içerirler.
281286
282287## Örnek Procedural Macro
283288
@@ -291,14 +296,15 @@ cargo add quote
291296cargo add syn -F full
292297```
293298
294- Ardından toml dosyasında bu küfenin bir procedural macro olarak ele alınması gerektiği bildirilir .
299+ Ardından ** toml** dosyasında bu küfenin procedural bir macro kütüphanesi olarak ele alınması gerektiği belirtilir .
295300
296301``` toml
297302[lib ]
298303proc-macro = true
299304```
300305
301- Aşağıda kodun çalışma zamanının ölçen bir işlevselliğin procedural macro olarak nasıl yazılabileceği örneklenmektedir.
306+ Aşağıda kodun çalışma zamanını ölçen bir işlevselliğin ** procedural macro** olarak nasıl yazılabileceği
307+ örneklenmektedir.
302308
303309``` rust
304310extern crate proc_macro;
@@ -325,15 +331,16 @@ pub fn work_time_effort(_attr: TokenStream, item: TokenStream) -> TokenStream {
325331}
326332```
327333
328- İlgili makro parametre olarak TokenStream'ler alır. Özellikle item değişkeni kod içerisinde kullanılır. item değişkeni
329- ile gelen TokenStream parse_macro_input! makrosu kullanılarak içeriği ele alınabilir bir türe dönüştürülür. Buradan
330- hareketle makronun uygulandığı metodun adı ve gövdesi yakalanbilir.
334+ İlgili makro parametre olarak ** TokenStream** 'ler alır. Özellikle ** item** değişkeni kod içerisinde kullanılır. item
335+ değişkeni ile gelen ** TokenStream** , ** parse_macro_input!** makrosu kullanılarak içeriği ele alınabilir bir türe
336+ dönüştürülür. Bir başka deyişle stream üzerinden gelen kod içerisindeki unsurlar özellik şeklinde ele alınabilir hale
337+ gelir. Buradan hareketle makronun uygulandığı metodun adı ve gövdesi yakalanmaktadır.
331338
332- İlerleyen adımda quote! makrosu ile yeni bir fonksiyon hazırlanır. Dikkat edileceği üzere gelen fonksiyon bloğunun
339+ İlerleyen adımda ** quote!** makrosu ile yeni bir fonksiyon hazırlanır. Dikkat edileceği üzere gelen fonksiyon bloğunun
333340öncesine ve sonrasına yeni kod parçaları eklenmektedir. Makro çıktı olarak üretilen yeni kod parçasını yine bir
334- TokenStream olarak dışarı verir.
341+ ** TokenStream** olarak dışarı verir.
335342
336- Bu makro herhangibir metot için aşağıdaki gibi kullanılabilir.
343+ Bu makro herhangi bir metot için aşağıdaki gibi kullanılabilir.
337344
338345``` rust
339346mod samples ;
@@ -354,8 +361,8 @@ fn main() {
354361}
355362```
356363
357- Bir procedural macro küfesini kullanmak için ilgili projeye dependency olarak bildirilmesi gerekir. Bunun için toml
358- dosyasındaki ilgili kısım değiştirilmelidir. Örneğin,
364+ Bir procedural macro küfesini kullanmak için ilgili projeye ** dependency** olarak bildirilmesi gerekir. Bunun için makro
365+ kütüphanesini kullanacak projenin ** toml ** dosyasında aşağıdaki gibi bir değişiklik yapılmalıdır.
359366
360367``` toml
361368[dependencies ]
0 commit comments