🚀 BAŞTAN SONA RUST DİLİ BÖLÜM-18: Makrolar (Macros) ve Meta-Programming 🧙‍♂️

Konu Özeti

Rust'ta makro sistemi ve meta-programlama konusu ele alınıyor. Bildirimsel makrolar (macro_rules!) ile değişken sayıda parametre alan, derleme zamanında genişleyen yapılar oluşturuluyor; örnek olarak vec! ve HashMap makroları inceleniyor. Prosedürel makroların (custom derive, attribute-like, function-like) AST üzerinde kod üretme mantığı açıklanıyor ve bir custom derive örneğiyle otomatik trait implementasyonu gösteriliyor. Makroların fonksiyonlara göre avantajları ve ne zaman tercih edilmeleri gerektiği değerlendiriliyor.

🚀 BAŞTAN SONA RUST DİLİ BÖLÜM-18: Makrolar (Macros) ve Meta-Programming 🧙‍♂️

Selamlar! Serimizin 18. bölümüne hoş geldiniz.

Önceki bölümde `tokio` runtime ile asenkron programlama, `async/await` mimarisi ve eşzamanlı TCP sunucu geliştirmeyi incelemiştik. Bu bölümde ise Rust’ın en güçlü ve büyüleyici özelliklerinden biri olan **Makro Sistemini (Macros)** ve **Meta-Programming** konusunu ele alacağız.

Makrolar, basitçe "kod yazan kodlar" olarak tanımlanabilir. Rust'ta makroları bildiğimiz standart fonksiyonlardan ayıran en büyük farklar şunlardır:
• **Değişken Sayıda Parametre (Variadic Arguments):** `println!` veya `vec!` gibi makrolara istediğiniz sayıda parametre geçebilirsiniz.
• **Derleme Zamanı Çalışma (Compile-Time Execution):** Makrolar kod derlenmeden önce genişletilir (macro expansion), dolayısıyla çalışma zamanında (runtime) ek hiçbir maliyet yaratmazlar.

Rust'ta iki ana makro türü vardır:
1. **Declarative Macros (Bildirimsel Makrolar):** `macro_rules!` ile yazılan desen eşlemeli yapılar.
2. **Procedural Macros (Prosedürel Makrolar):** Kod üzerinde AST (Abstract Syntax Tree) düzeyinde işlem yapan yapılar.

---

📌 1. Declarative Macros (`macro_rules!`) 🎯

Declarative Makrolar, Rust'ta en sık kullanılan ve `match` ifadesine benzer bir desen eşleme (pattern matching) mantığıyla çalışan yapılardır.

Basit bir vektör oluşturma makrosu mantığını inceleyelim:

macro_rules! benim_vec {
( $( $x:expr ),* ) => {
{
let mut gecici_vec = Vec::new();
$(
gecici_vec.push($x);
)*
gecici_vec
}
};
}

fn main() {
let v = benim_vec![1, 2, 3, 4];
println!("Oluşturulan Vektör: {:?}", v);
}

#### Makro Dizilim Mantığı ve Tasarımcılar (Designators):
Yukarıdaki makroda yer alan işaretlerin anlamı şudur:
• `$x:expr`: `x` adında bir ifade (expression) yakalar.
• `$( ... ),*`: Virgülle ayrılmış sıfır veya daha fazla öğeyi tekrarlayarak yakalar (`*` = 0 veya daha fazla, `+` = 1 veya daha fazla).
• `$x:expr` ifadesi her tekrarda bir kez genişletilir ve `gecici_vec.push(...)` komutuna dönüşür.

Sık kullanılan tasarımcı tipleri (Designators):
- `expr`: Herhangi bir ifade (örn: `1 + 2`, `"metin"`, `x.foo()`)
- `ident`: Tanımlayıcı / Değişken veya fonksiyon adı (örn: `sayi`, `kullanici_adi`)
- `ty`: Veri tipi (örn: `i32`, `String`, `Vec<u8>`)
- `pat`: Desen / Pattern (örn: `Some(x)`, `_`)
- `stmt`: Bir deyim / Statement (örn: `let x = 5;`)

---

⚙️ 2. Gelişmiş Bildirimsel Makro Örneği 🛠️

Aşağıdaki örnekte birden fazla deseni eşleyebilen esnek bir harita (HashMap) makrosu oluşturalım:

use std::collections::HashMap;

macro_rules! hashmap {
( $( $key:expr => $val:expr ),* $(,)? ) => {
{
let mut map = HashMap::new();
$(
map.insert($key, $val);
)*
map
}
};
}

fn main() {
let harita = hashmap! {
"Mavi" => 10,
"Kırmızı" => 20,
"Yeşil" => 30, // Sondaki virgül de '$(,)?' sayesinde desteklenir
};

println!("{:?}", harita);
}

---

🧬 3. Procedural Macros (Prosedürel Makrolar) 🏗️

Prosedürel makrolar bir işlev gibi çalışır: Rust kodunu girdi (TokenStream) olarak alır, derleme zamanında üzerinde matematiksel/mantıksal işlemler yapar ve geriye yeni bir Rust kodu (TokenStream) üretir.

Procedural Makrolar kendi özel kütüphane projelerinde (`proc-macro = true` tanımlı `Cargo.toml`) yazılmalıdır.

3 farklı türe ayrılırlar:
1. **Custom Derive (`#[derive(MyTrait)]`):** Struct veya Enum'lara otomatik Trait implementasyonu ekler.
2. **Attribute-like (`#[route(GET, "/")]`):** Kod ögelerine özel nitelik/annotation eklememizi sağlar.
3. **Function-like (`sql!("SELECT * FROM users")`):** Fonksiyon çağrısı şeklinde çalışan gelişmiş makrolardır.

---

🛠️ 4. Custom Derive Makro Yapısı 🎨

Gerçek hayatta en sık karşılaştığımız `#[derive(Serialize, Deserialize)]` veya `#[derive(Debug)]` gibi yapılar birer Custom Derive makrosudur.

Prosedürel bir makronun temel yapısı şu şekildedir:

use proc_macro::TokenStream;
use quote::quote;
use syn;

#[proc_macro_derive(Yazdirilabilir)]
pub fn yazdirilabilir_derive(input: TokenStream) -> TokenStream {
// 1. Gelen AST (Abstract Syntax Tree) yapısını ayrıştır
let ast = syn::parse(input).unwrap();

// 2. Trait uygulamasını oluştur
impl_yazdirilabilir(&ast)
}

fn impl_yazdirilabilir(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl Yazdirilabilir for #name {
fn yazdir(&self) {
println!("Bu nesne bir '{}' yapısıdır!", stringify!(#name));
}
}
};
gen.into()
}

Kullanımı:

#[derive(Yazdirilabilir)]
struct Kullanici {
ad: String,
}

fn main() {
let k = Kullanici { ad: String::from("Ahmet") };
k.yazdir(); // Makro tarafından otomatik türetilen metot!
}

---

⚖️ 5. Makrolar vs Fonksiyonlar: Ne Zaman Hangisi? 📊

| Özellik | Fonksiyonlar (`fn`) | Makrolar (`macro_rules!`) |
| :--- | :--- | :--- |
| Değişken Parametre | Desteklemez (Sabit sayıda) | Esnek (Değişken sayıda) |
| Derleme Süresi | Hızlı | Kod üretimi nedeniyle bir miktar yavaşlatabilir |
| Çalışma Zamanı Maliyeti | Standart çağrı maliyeti | Sıfır (Zero Runtime Cost) |
| Hata Okunabilirliği | Net ve açık hata mesajları | Karmaşık makro hata mesajları oluşabilir |
| Tip Güvenliği | Çağrı anında tip kontrolü | Derleme genişletme anında kontrol |

> **Genel İlke:** Eğer yapmak istediğiniz işlem normal bir fonksiyon veya Trait ile çözülebiliyorsa öncelikle fonksiyon tercih edilmelidir. Makrolar yalnızca değişken sayıda parametre, derleme zamanı kod üretimi veya Domain-Specific Language (DSL) yazımı gerektiğinde kullanılmalıdır.

---

📝 Bölüm Özeti ve Gelecek Bölüm 🔮

Bu bölümde Declarative Makro yapısını (`macro_rules!`), desen eşleme ve tasarımcıları (designators), Procedural Makroların mantığını ve Custom Derive mimarisini öğrendik.

👉 Bölüm 19'da Bizi Ne Bekliyor?
• Sistem Seviyesi ve Düşük Seviyeli Programlama: **Unsafe Rust ve FFI (Foreign Function Interface)** ☣️
• Bare-metal / Low-level konseptler
• Raw Pointers (`*const T`, `*mut T`)
• C küt
üphaneleri ile entegrasyon (`extern "C"`) ve bellek ihlali yönetimi

Sorularınızı konu altından iletebilirsiniz. Serimizin bir sonraki bölümünde görüşmek üzere! 🦀✨

🔒 Bu içeriği görmek için giriş yapın

 
Yanıt yazmak için giriş yapmalısınız
Forum özelliklerini kullanmak ve Level 2 üyelik satın almak için hesabınıza giriş yapın.
135,050Konular
3,297,719Mesajlar
326,237Kullanıcılar
thekral21Son Üye
Üst Alt