Macros (101): println! ve Diğer Makroların Arkasındaki Sır

  • Konbuyu başlatan Konbuyu başlatan irfo
  • Başlangıç tarihi Başlangıç tarihi

irfo

Moderatör
Top Poster Of Month
Katılım
7 Ocak 2026
Mesajlar
290
Tepkime puanı
2
Puanları
18
Rust'ta println!, vec! veya panic! gibi ifadelerin sonundaki ünlem işareti (!), bunların normal fonksiyonlar değil, birer makro olduğunu fısıldar.
Fonksiyonlar değerler üzerinde çalışırken, makrolar kodun kendisi üzerinde çalışır. Yani makrolar, henüz kod derlenmeden önce sizin yerinize daha fazla kod yazan "kod üreten kodlar"dır.

1. Neden Makro Kullanırız? (Fonksiyonlardan Farkı)​

Makroları fonksiyonlardan ayıran üç temel süper güç vardır:
  1. Değişken Sayıda Argüman: Bir fonksiyonun parametre sayısı sabittir, ancak println! içine istediğiniz kadar değişken atabilirsiniz.
  2. Meta-programlama: Makrolar, derleme aşamasında tip tanımlarını okuyabilir ve otomatik olarak impl blokları (örneğin #[derive(Debug)]) oluşturabilir.
  3. Performans: Makrolar derleme zamanında "açılır" (expand), bu yüzden çalışma zamanında (runtime) bir fonksiyon çağrısı maliyeti getirmezler.

2. Makro Türleri​

Rust'ta iki ana makro türü bulunur:

A. Bildirimsel Makrolar (Declarative Macros)​

En yaygın kullanılan türdür. macro_rules! anahtar kelimesiyle tanımlanırlar. Bir match ifadesine benzer şekilde çalışırlar: Belirli bir kalıba uyan kod gelirse, onu başka bir koda dönüştürürler.
Rust
Kod:
macro_rules! selamla {
    // Hiç argüman verilmezse
    () => {
        println!("Merhaba!");
    };
    // Bir isim verilirse
    ($isim:expr) => {
        println!("Merhaba, {}!", $isim);
    };
}

fn main() {
    selamla!();          // Çıktı: Merhaba!
    selamla!("Dünya");    // Çıktı: Merhaba, Dünya!
}

B. Prosedürel Makrolar (Procedural Macros)​

Bu makrolar daha gelişmiştir; girdi olarak aldıkları Rust kodunu bir "token akışı" (token stream) olarak işler ve üzerinde karmaşık değişiklikler yaparlar. Üç çeşittir:
  • Derive Makroları: #[derive(Serialize)] gibi, bir struct üzerine otomatik özellik ekler.
  • Attribute-like: #[tokio::main] gibi, fonksiyonların davranışını değiştirir.
  • Function-like: sqlx::query!(...) gibi, fonksiyon gibi çağrılır ama içinde karmaşık mantıklar (örneğin SQL kontrolü) çalıştırır.

3. println! Makrosunun Sırrı​

Neden println bir fonksiyon değildir? Çünkü Rust'ın tip güvenliği (type safety) kuralları, bir fonksiyonun çalışma zamanında format string içindeki {} sayısı ile argüman sayısını kontrol etmesine izin vermez.
println! makrosu, derleme anında string'i analiz eder:
  1. Eğer {} sayısı ile argümanlar uyuşmuyorsa derleme hatası verir.
  2. Argümanların Display veya Debug trait'ine sahip olup olmadığını kontrol eder.
  3. Arka planda std::io::stdout() yazma işlemlerini sizin yerinize optimize ederek kodunuzu genişletir.

4. Makro Yazarken Dikkat Edilmesi Gerekenler​

  • Okunabilirlik: Makrolar kodun ne yaptığını gizleyebilir, bu yüzden sadece fonksiyonların yetmediği yerde kullanılmalıdır.
  • Hata Ayıklama: Makro içindeki hataları bulmak zordur. cargo expand komutunu kullanarak makroların arkasında üretilen asıl kodu görebilirsiniz.

Özet Tablo​

ÖzellikFonksiyonlarMakrolar
Sözdizimiisim()isim!()
ParametrelerSabit sayıda ve tipteDeğişken sayıda (Variadic)
Çalışma ZamanıÇağrı anında (Runtime)Derleme anında (Compile-time)
Kod ÜretimiYapamazYapabilir
 
Geri
Üst