---
title: 'Bildirimsel Makrolar'
source: 'https://academia.sh/tr/kurslar/rust-ileri/bildirimsel-makrolar'
course: 'Rust Ekosistemi ve İleri Konular'
language: tr
updated: '2026-10-21T05:03:45+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bildirimsel makronun ürettiği gövdenin, elle yazılmış bir gövdeyle aynı biçimde tip denetiminden geçtiğini koşturarak gösterir'
    - 'Makro hijyeninin, makro içindeki bir adın çağıranın aynı adlı değişkenini bozmadığını doğrular'
    - 'Aynı makronun iki farklı türle çağrıldığında ikisinin de derlendiğini, ama makronun hangisinin anlamlı olduğunu bilmediğini örnekle açıklar'
---

# Bildirimsel Makrolar

Kursun ölçüm çekirdeği program içinden koşturulur ve on iki gözlemi dört sınıfa yerleştirir — sürdürüyor, devredildi, dilin dışında, denetlenmiyor; dağılım 4/3/2/3'tür ve bu ders yalnız 'sürdürüyor' sınıfına düşen iki gözlemi derinleştirir: makronun ürettiği gövde tip denetiminden geçiyor, makronun içindeki ad çağıranınkini bozmuyor.

Eşzamanlılık ve Asenkron Rust kursu boyunca bir işin ilerlemek için neye ihtiyaç duyduğu
ölçüldü — bir kanal, bir kilit, bir bekleme noktası. Dört kurs boyunca hiç sorgulanmayan tek
bir varsayım vardı: derleyici arkada duruyor ve her adımı denetliyor. Bu kurs tam o varsayımı
ölçüye açıyor. On iki farklı düzenekte aynı soru sorulacak: derleyicinin güvencesi burada
nerede bitiyor?

Yanıt dört sınıftan birine düşüyor ve bu dört ad kurs boyunca değişmiyor. Bu ilk ders on
ikisini birlikte, tek bir çekirdek programla koşturuyor; sonra ikisini — makronun ürettiği
gövdenin denetimi ve makronun kendi iç adlarının çağıranınkiyle karışmaması — derinleştiriyor.
Kalan on soru bu kursun sonraki on bir dersine düşüyor; bu ders yalnız makrolarla ilgili
ikisini alıyor, çünkü bu konu makrolarla ilgili.

## Dört Sınıf

| Sınıf | Tanım |
|---|---|
| sürdürüyor | Derleyici denetlemeye devam ediyor |
| devredildi | Denetim durdu; koşulu yazan üstlendi |
| dilin dışında | Doğruluğu başka bir dil, araç ya da hedef belirliyor |
| denetlenmiyor | Hiç kimse denetlemiyor |

Bu dörtlü, Standart Kütüphane ve Akışlar kursunda (M08/K03) sorulan soruyla karışmaz: orada
bir davranışın garantisini kimin verdiği — arayüz mü, gerçekleştirim mi, çağıran mı —
soruluyordu, yani bir sözleşmenin tarafları. Burada sorulan, derleyicinin kendi denetiminin
nerede durduğu. Bildirimsel makrolar bu dört sınıfın ilkine, sürdürüyor sınıfına, en açık
örneği veriyor: bir makro çağrısının ürettiği kaynak, elle yazılmış bir kaynaktan derleyici
açısından ayırt edilemez ve aynı kurallarla denetlenir.

Bunun neden şaşırtıcı olmadığını görmek için makronun ne zaman devreye girdiğine bakmak
gerekiyor. Bir bildirimsel makronun genişlemesi, tür denetiminden **önce** oluyor: derleyici
önce kaynaktaki her makro çağrısını kendi ürettiği metinle değiştiriyor, sonra bütün dosyayı
sıradan Rust kaynağı gibi okuyup türleri, ödünç kurallarını ve isim çözümlemesini sırasıyla
uyguluyor. Bu sıralama, makronun ürettiği kaynağın neden "sürdürüyor" sınıfına düştüğünü
açıklıyor: makro genişlediği anda ortadan kalkıyor, geriye yalnız sıradan Rust kaynağı
kalıyor ve o kaynağı denetleyen mekanizma, elle yazılmış bir dosyayı denetleyenle aynı.

## Ölçüm Çekirdeği

Kursun geri kalanında kullanılan çekirdek `guvence.rs`dir. On iki gözlemi tek bir gözlem
defterine yazar ve sonunda sınıf başına toplamı basar. Üçüncü taraf kasa kullanmaz: yabancı
fonksiyon arayüzü, C kitaplığının zaten bağlanmış iki işleviyle kurulur, dışarıdan hiçbir
paket derlemeye girmez. Kursun geri kalan on bir dersi bu çekirdeğin yalnız kendi kesitini
alacak; yalnız bu ders, on iki gözlemin tamamını bir arada gösteriyor.

**MK1.** `gozle` işlevi ve `defter` demeti (`String`, `String`, `String` üçlüsü) çekirdeğin
kendi tasarım kararıdır. Bir gözlemin hangi sınıfa girdiğine karar veren şey her satırın
kaynaktaki biçimidir; `gozle` çağrısı yalnız bu kararı bir tabloya kaydeder ki on iki gözlem
tek koşumda karşılaştırılabilsin.

```rust
// guvence.rs — derleyicinin guvencesi nerede bitiyor: surduruyor mu, yazana mi devredildi,
// dilin disinda mi, yoksa hic kimse denetlemiyor mu.

use std::ffi::CString;
use std::os::raw::{c_char, c_int};

// Dilin disindaki iki islev: govdelerini derleyici hic gormedi.
extern "C" {
    fn abs(x: c_int) -> c_int;
    fn strlen(s: *const c_char) -> usize;
}

// Bildirimsel makro: urettigi govde de tip denetiminden geciyor.
macro_rules! ikiye_katla {
    ($ad:ident) => {
        fn $ad(x: i32) -> i32 {
            x * 2
        }
    };
}

ikiye_katla!(uretilen_islev);

// Makro hijyeni: icerideki ad, cagiranin adiyla carpismiyor.
macro_rules! kendi_adini_kullan {
    () => {{
        let x = 99;
        x
    }};
}

/// Bu belge satiri islevin **en buyuk** degeri dondurdugunu soyluyor.
/// Islev en kucugu donduruyor. Hicbir arac bu celiskiyi denetlemiyor.
fn belgesi_yanlis(a: i32, b: i32) -> i32 {
    if a < b {
        a
    } else {
        b
    }
}

// cfg ile disarida kalan govde: derleyici bunu hic gormuyor.
#[cfg(feature = "acilmayan")]
fn hic_denetlenmeyen() {
    bu_islev_hic_tanimlanmadi();
    let _x: i32 = "metin";
}

const SURUM_DIZESI: &str = "0.1.0";

// Sozlesmesi yazana devredilen islev: cagiran indeksin gecerli oldugunu garanti eder.
unsafe fn ikinci_ogeyi_al(v: &[i32]) -> i32 {
    *v.get_unchecked(1)
}

fn gozle(defter: &mut Vec<(String, String, String)>, ad: &str, sinif: &str, kanit: String) {
    defter.push((ad.to_string(), sinif.to_string(), kanit));
}

fn main() {
    let mut defter: Vec<(String, String, String)> = Vec::new();
    let v = vec![10, 20, 30, 40];

    // --- derleyici surduruyor ---

    let uretilen = uretilen_islev(21);
    gozle(
        &mut defter,
        "makro uretilen govde",
        "surduruyor",
        format!("cagri sonucu {}", uretilen),
    );

    let x = 1;
    let makro_degeri = kendi_adini_kullan!();
    gozle(
        &mut defter,
        "makro hijyeni",
        "surduruyor",
        format!("disaridaki x={} makro {}", x, makro_degeri),
    );

    let sinirda = v.get(10);
    gozle(
        &mut defter,
        "dilim sinir denetimi",
        "surduruyor",
        format!("onuncu oge {:?}", sinirda),
    );

    let isaretci = v.as_ptr();
    let (ilk_ham, blok_ici) = unsafe {
        // Ayni blokta iki islem: biri gercekten denetimsiz, oteki hala denetleniyor.
        (*isaretci, v.get(10))
    };
    gozle(
        &mut defter,
        "unsafe blok icindeki denetim",
        "surduruyor",
        format!("ham okuma {} ama onuncu oge {:?}", ilk_ham, blok_ici),
    );

    // --- yazana devredildi ---

    let ham = unsafe { *isaretci.add(2) };
    gozle(
        &mut defter,
        "ham isaretci okuma",
        "devredildi",
        format!("okunan {} guvenli karsiligi {}", ham, v[2]),
    );

    let denetimsiz = unsafe { *v.get_unchecked(1) };
    gozle(
        &mut defter,
        "denetimsiz erisim",
        "devredildi",
        format!("okunan {} guvenli karsiligi {}", denetimsiz, v[1]),
    );

    let sozlesmeli = unsafe { ikinci_ogeyi_al(&v) };
    gozle(
        &mut defter,
        "sozlesmesi devredilen islev",
        "devredildi",
        format!("okunan {}", sozlesmeli),
    );

    // --- dilin disinda ---

    let mutlak = unsafe { abs(-7) };
    gozle(
        &mut defter,
        "yabanci islev cagrisi",
        "dilin disinda",
        format!("sonuc {}", mutlak),
    );

    let metin = CString::new("merhaba").unwrap();
    let uzunluk = unsafe { strlen(metin.as_ptr()) };
    gozle(
        &mut defter,
        "yabanci uzunluk olcumu",
        "dilin disinda",
        format!("uzunluk {} rust karsiligi {}", uzunluk, "merhaba".len()),
    );

    // --- kimse denetlemiyor ---

    let belge_sonucu = belgesi_yanlis(3, 9);
    gozle(
        &mut defter,
        "belge metninin dogrulugu",
        "denetlenmiyor",
        format!("belge en buyuk diyor, sonuc {}", belge_sonucu),
    );

    gozle(
        &mut defter,
        "surum dizesinin anlami",
        "denetlenmiyor",
        format!("yazili deger {}", SURUM_DIZESI),
    );

    gozle(
        &mut defter,
        "derlemeye girmeyen govde",
        "denetlenmiyor",
        String::from("tanimsiz cagri ve tip hatasi tasiyor, program yine de derlendi"),
    );

    // --- rapor ---

    println!("{:<32}{:<16}{}", "gozlem", "sinif", "kanit");
    for (ad, sinif, kanit) in &defter {
        println!("{:<32}{:<16}{}", ad, sinif, kanit);
    }

    println!();
    for s in ["surduruyor", "devredildi", "dilin disinda", "denetlenmiyor"] {
        let adet = defter.iter().filter(|(_, k, _)| k == s).count();
        println!("{:<16}{}", s, adet);
    }
    println!("toplam gozlem: {}", defter.len());
}
```

```
gozlem                          sinif           kanit
makro uretilen govde            surduruyor      cagri sonucu 42
makro hijyeni                   surduruyor      disaridaki x=1 makro 99
dilim sinir denetimi            surduruyor      onuncu oge None
unsafe blok icindeki denetim    surduruyor      ham okuma 10 ama onuncu oge None
ham isaretci okuma              devredildi      okunan 30 guvenli karsiligi 30
denetimsiz erisim               devredildi      okunan 20 guvenli karsiligi 20
sozlesmesi devredilen islev     devredildi      okunan 20
yabanci islev cagrisi           dilin disinda   sonuc 7
yabanci uzunluk olcumu          dilin disinda   uzunluk 7 rust karsiligi 7
belge metninin dogrulugu        denetlenmiyor   belge en buyuk diyor, sonuc 3
surum dizesinin anlami          denetlenmiyor   yazili deger 0.1.0
derlemeye girmeyen govde        denetlenmiyor   tanimsiz cagri ve tip hatasi tasiyor, program yine de derlendi

surduruyor      4
devredildi      3
dilin disinda   2
denetlenmiyor   3
toplam gozlem: 12
```

**MK2.** On iki gözlemin dördü sürdürüyor sınıfına düşüyor ve bu dörtten ikisi bu konunun
kapsamında: üretilen bir gövdenin çağrısı (`cagri sonucu 42`) ve makro içindeki bir adın
çağıranın aynı adlı değişkenini bozmaması (`disaridaki x=1 makro 99`). Bu ders geri kalan
onunu bir kez koşturup bir kenara bırakıyor, yalnız bu ikisini derinleştiriyor.

## Üretilen Gövdenin Tip Denetimi

Çekirdekteki üretim makrosunun çağrısı, `uretilen_islev` adlı bir işlevi hiçbir elle yazılmış
`fn` satırı olmadan var ediyor. Bu işlevin `uretilen_islev(21)` çağrısında `42` döndürmesi,
kaynakta bir gövdenin durduğunu gösteriyor ama bunun ne kadar derine indiğini göstermiyor.
Aşağıdaki örnek aynı üretimi bir kez daha kurup üretilen işlevi, makronun hiç bilmediği bir
kısıtlamanın — genel bir sınırın — içine sokuyor.

```rust
// govde_genelleme.rs — makro uretimi bir govde, genel bir sinirlamaya sokuluyor.
macro_rules! govde_uret {
    ($ad:ident) => {
        fn $ad(x: i32) -> i32 {
            x * 3
        }
    };
}

govde_uret!(uc_katla);

fn uygula<F: Fn(i32) -> i32>(islev: F, deger: i32) -> i32 {
    islev(deger)
}

fn main() {
    let sonuc = uygula(uc_katla, 7);
    println!("uc_katla genel sinir icinde: {}", sonuc);
}
```

```
uc_katla genel sinir icinde: 21
```

**MK3.** Üretim makrosunun bu çağrısı yalnız `uc_katla` adlı bir işlevi doğurdu; işlevin `uygula` içindeki
`F: Fn(i32) -> i32` sınırını sağlayıp sağlamadığını makronun kendisi hiç sınamadı — bu sınama
üretilen kaynak `uygula`ya geçirildiğinde, sıradan bir işlev geçirilmiş gibi, derleyicinin
kendisi tarafından yapıldı. Üretilen gövde yalnız doğrudan çağrıldığında değil, bir genel
sınırın içine sokulduğunda da denetleniyor: makro hangi bağlamda kullanılacağını önceden
bilmiyor, ama ürettiği kaynak her bağlamda aynı kurallara tabi.

Bu ayrım küçük görünse de sonucu büyük: üretim makrosunu tanımlayan yer, `uc_katla`nın hangi
genel sınırlara gireceğini hiç bilmiyor — o sınır çok sonra, tamamen ayrı bir yerde, `uygula`
tanımlanırken yazılıyor. Buna rağmen ikisi tutarlı çalışıyor, çünkü aralarındaki tek bağ
üretilen kaynağın **imzası**: `fn uc_katla(x: i32) -> i32`. Makro bu imzayı üretmekten
sorumlu, imzanın nerede kullanılacağından değil; kalan her şeyi, tıpkı elle yazılmış bir
işlevde olduğu gibi, çağıran taraf ve derleyici birlikte çözüyor.

## Makro Hijyeni

Çekirdekteki hijyen makrosunun çağrısı, kendi içinde bir `x` bağlıyor ve onu döndürüyor;
çağıranın kendi `x`i (`1`) bu çağrıdan etkilenmiyor (`disaridaki x=1 makro 99`). Bu tek bir
çağrıyla gösterilen bir sonuç. Aşağıdaki örnek aynı makroyu aynı kapsamda iki kez çağırarak
hem dıştaki adla hem birbirleriyle çakışıp çakışmadığını sınıyor.

```rust
// hijyen_ikinci.rs — ayni makro iki kez cagriliyor, ic adlari ne disariyla ne kendi aralarinda karisiyor.
macro_rules! kendi_adini_kullan {
    () => {{
        let x = 99;
        x
    }};
}

fn main() {
    let x = 7;
    let birinci = kendi_adini_kullan!();
    let ikinci = kendi_adini_kullan!() + 1;
    println!("disaridaki x={} birinci={} ikinci={}", x, birinci, ikinci);
}
```

```
disaridaki x=7 birinci=99 ikinci=100
```

Dıştaki `x` yedi olarak kalıyor, iki çağrının ikisi de kendi `99`unu üretiyor ve ikinci çağrı
bu değeri kendi hesabında (`+ 1`) kullanabiliyor — makronun içindeki ad her genişlemede kendi
kapsamına ait, ne dıştaki `x`le ne bir önceki genişlemenin `x`iyle karışıyor. Hijyen burada
bir isim çakışmasını önleyen bir kural değil, her makro genişlemesinin kendi adlandırma
alanını taşıdığı bir düzenek: metin düzeyinde aynı `x` sözcüğü yan yana üç kez geçiyor, ama
üçü de derleyici için ayrı birer bağlama.

Bu düzeneğin pratik karşılığı şu: bir makro tanımlayan taraf, kendi gövdesinde istediği kadar
yardımcı ad kullanabiliyor ve çağıranın hangi adları kullandığını hiç bilmek zorunda kalmıyor.
Hijyen olmasaydı, makronun içindeki her yardımcı adın çağıranın kapsamındaki adlarla
çakışmadığından ayrıca emin olmak gerekirdi — bu da makroyu, adları önceden tahmin edilemeyen
her yerde kullanmayı riskli kılardı. Rust'ın makro sistemi bu riski dilin kendisinde, çağıran
ile makro yazan arasında bir sözleşme yazmadan kapatıyor.

## Sınırlayıcı Ölçüm

Önceki iki bölüm, makronun ürettiği kaynağın gerçek Rust kaynağı gibi denetlendiğini gösterdi.
Bu denetim bir sınır taşıyor: makro yalnız sözdizimi düzeyinde çalışıyor, yerine koyduğu
parçanın ne anlama geldiğini bilmiyor. Aynı makro iki ayrı türle çağrılıp bu sınır gösterilir.

```rust
// buyuk_secici.rs — ayni makro iki turde cagriliyor; ikisi de tip denetiminden geciyor.
macro_rules! buyugu_secen_govde_uret {
    ($ad:ident, $t:ty) => {
        fn $ad(a: $t, b: $t) -> $t {
            if a > b { a } else { b }
        }
    };
}

buyugu_secen_govde_uret!(buyuk_tam_sayi, i32);
buyugu_secen_govde_uret!(buyuk_mantiksal, bool);

fn main() {
    println!("buyuk_tam_sayi(3, 9)={}", buyuk_tam_sayi(3, 9));
    println!("buyuk_mantiksal(false, true)={}", buyuk_mantiksal(false, true));
}
```

```
buyuk_tam_sayi(3, 9)=9
buyuk_mantiksal(false, true)=true
```

Makronun kendisinde hiçbir tür sınırı yazılı değil; `$t:ty` yalnız "burada bir tür adı
gelecek" diyor, hangi türlerin bu işlemi anlamlı kılacağını sormuyor. `i32` için büyüğü
seçmek sıradan bir karşılaştırma; `bool` için de aynı sözdizimi geçerli — `false`, `true`den
küçük sayılıyor ve ikinci işlev `true` döndürüyor. Derleyici ikisini de aynı gerekçeyle kabul
ediyor: her ikisi de karşılaştırma işlecini destekleyen bir tür. Hangi çağrının çağıran için
gerçekten bir anlam taşıdığını, hangisinin yalnız türü uyduğu için derlendiğini makro da
derleyici de söyleyemiyor — bu ayrımı yalnız kaynağı okuyan taraf yapabiliyor.

Bu sınır, önceki iki bölümde gösterilen denetimi geçersiz kılmıyor; onu **daraltıyor**.
Üretilen gövdenin bir genel sınıra uyup uymadığı, içindeki adın çağıranınkiyle çakışıp
çakışmadığı — ikisi de kesin sorulardır ve derleyici ikisini de kesin yanıtlıyor. Ama "bu
işlem bu tür için mantıklı mı" sorusu bambaşka bir sorudur ve derleyicinin elindeki tek araç
tür uygunluğu: bir tür karşılaştırma işlecini destekliyorsa, o karşılaştırmanın çağıranın
niyetine uyup uymadığına bakmadan kabul ediyor. Sözdizimi düzeyinde çalışan bir araç için bu
beklenmedik bir sınır değil, tanımın kendisi — makro bir metni başka bir metne dönüştürüyor,
o metnin okuyan için ne ifade ettiğine karışmıyor.

## Özet

- Kurs boyunca dört sınıf değişmiyor: sürdürüyor, devredildi, dilin dışında, denetlenmiyor;
  on iki gözlemin dağılımı 4/3/2/3'tür.
- Bu ders sürdürüyor sınıfına düşen iki gözlemi derinleştirdi: makronun ürettiği gövde bir
  genel sınırın içinde bile sıradan kaynak gibi denetleniyor, makronun içindeki ad her
  genişlemede kendi kapsamını taşıyor ve çağıranınkiyle karışmıyor.
- Üretilen gövdenin denetimi doğrudan çağrıyla sınırlı değil: üretilen bir işlev, bir trait
  sınırının içine sokulduğunda da aynı kurallarla sınanıyor.
- Makro sözdizimi düzeyinde çalışıyor: aynı makro birbirinden anlamca uzak iki türle
  çağrıldığında ikisi de derleniyor, çünkü derleyici yalnız türlerin uyup uymadığına bakıyor.

## Sonraki Adım

Bu ders, üretimin aynı derleme adımında — aynı dosyada — gerçekleştiğini gösterdi: makro
tanımı ve makronun çağrısı bir arada durabiliyordu, derleyici tek bir çağrıda hem tanımı hem
genişlemeyi hem tür denetimini yapabiliyordu. Sıradaki ders aynı "sürdürüyor" sınıfını
bambaşka bir kurgudan ölçüyor: üretim artık aynı dosyada değil, ayrı bir derleme adımında
oluyor ve tüketen kaynak üretilen kaynağı ancak derleyici ikinci kez çağrıldığında görebiliyor.
