---
title: 'Rust Ekosistemi ve İleri Konular'
source: 'https://academia.sh/tr/kurslar/rust-ileri'
language: tr
updated: '2026-09-13T05:09:26+00:00'
license: 'CC BY-SA 4.0'
lessons: 12
outcomes:
    - 'Makrolarla yinelenen kodu üretir'
    - 'Derleyici güvencesinin nerede bittiğini ölçerek ayırt eder'
    - 'unsafe bloğun sözleşmesini ve sorumluluğunu açıklar'
    - 'Kütüphane paketleyip belgelendirerek yayımlar'
---

# Rust Ekosistemi ve İleri Konular

Bir dilin verdiği güvence her yerde aynı ölçüde geçerli değildir: kurs her düzeneği tek bir soruyla ölçer — derleyicinin güvencesi burada nerede bitiyor? Makrolar, unsafe kod, yabancı fonksiyon arayüzü, farklı hedefler, sınama, belgelendirme ve yayımlama aynı çerçeveden geçirilir.

## Makrolar ve Metaprogramlama

Desen tabanlı kod üretiminin bir şablondan çok bir dil kesiti olması, öznitelik ve türetme biçimindeki üretimin ayrı bir derleme adımına taşınması, ve üretilen sözdiziminin okunabilir bir arayüz kurmakta kullanılması.

### Bildirimsel Makrolar

Source: https://academia.sh/tr/kurslar/rust-ileri/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.

### Prosedürel Makrolar

Source: https://academia.sh/tr/kurslar/rust-ileri/prosedurel-makrolar

Bir program std::process::Command ile derleyiciyi iki kez çağırır — önce bir prosedürel makro kasasını, sonra onu kullanan kaynağı — ve yalnız derlendi/derlenmedi ile çıktı eşleşmesini basar; üretilen gövde yine sürdürüyor sınıfına düşer, ama bu kez üretim aynı dosyada değil, ayrı bir derleme adımında gerçekleşir.

Önceki ders, bir makro tanımının ve makronun çağrısının aynı dosyada durabildiğini gösterdi:
derleyici tek bir çağrıda hem tanımı okudu, hem genişletti, hem de ürettiği kaynağı denetledi.
Bu ders aynı "sürdürüyor" sınıfını bambaşka bir kurgudan ölçüyor. Bir prosedürel makro, kaynak
metnini elle işleyen ve yeni kaynak metni döndüren sıradan bir Rust işlevidir — ama bu işlev
kendi başına ayrı bir kasada (crate) derlenmiş olmak zorundadır ve onu kullanan kaynak, ancak
derleyici o kasayı çoktan derlenmiş bir ikili olarak bulduğunda çalışabilir. Üretim artık aynı
okumada değil, iki ayrı derleme adımında gerçekleşiyor.

Bu iki adımlılık, dilin kendi kelime dağarcığında da görünür. Bildirimsel makrolar dilin
kendi desen eşleme düzeneğiyle çalışıyordu — bir `match` dalının bir değerle karşılaştığı
gibi, gelen andaçlar bir desenle karşılaştırılıyor ve yerine sabit bir kalıp konuyordu.
Prosedürel bir makro böyle bir desen eşleme düzeneğine bağlı değil: girdi olarak aldığı
andaç akışını istediği
herhangi bir Rust kodu gibi işleyebilir — döngü kurabilir, koşul yazabilir, bir yardımcı veri
yapısında biriktirebilir — ve sonunda yeni bir andaç akışı üretip döndürebilir. Bu, bildirimsel
makronun sözdizimsel şablonculuğundan farklı bir güç: üretim artık **programlanabilir**. Bu
gücün bedeli de burada başlıyor — programlanabilir bir üretim, derleyicinin çalıştırabileceği
bağımsız bir program gerektiriyor ve bağımsız bir programın kaynağın geri kalanıyla aynı
okumada var olması mümkün değil.

Bu farkı tek bir soruyla özetlemek mümkün: bir bildirimsel makro yalnız **desen eşleyip yer
değiştirir**, bir prosedürel makro ise **koşar**. Aradaki fark, çekirdeğin hangi araçla
üretildiğine değil, üretimin ne zaman ve nerede gerçekleştiğine bakan bu kursun ölçü ekseniyle
birebir örtüşüyor.

#### Üretim Neden Ayrı Bir Adıma Taşınıyor

Bir prosedürel makro işlevi, girdi olarak bir andaç akışı (`TokenStream`) alır ve çıktı olarak
başka bir andaç akışı döndürür. Bu dönüşüm çalışma zamanında değil, **derleme zamanında**
çalışır — ama çalışması için derleyicinin kendisinin, henüz derlemekte olduğu kaynağı işlerken,
bu işlevi bir alt program gibi **çağırabilmesi** gerekir. Bir işlevin derleme sürecinin
ortasında çağrılabilmesi için, o işlevin derleme başlamadan önce zaten bağlanabilir bir ikili
olarak var olması gerekiyor — kaynağın geri kalanıyla aynı okumada, aynı anda derlenmesi
mümkün değil. Bu yüzden dilin kendisi bir kısıtlama koyuyor: prosedürel makro içeren bir kasa,
`proc-macro` adlı ayrı bir kasa türüyle derlenmek zorunda ve onu kullanan her kasa, bu kasayı
kendi derlemesine bir bağımlılık olarak, önceden derlenmiş halde almak zorunda.

Bu, önceki dersteki bildirimsel makrodan temel bir ayrım. Orada makro tanımı ve çağrısı aynı
dosyada durabiliyordu, çünkü genişleme metinsel bir yer değiştirmeydi ve derleyici bunu tek
okumada yapabiliyordu. Burada üretim bir metin yer değiştirmesi değil, çalıştırılan bir
programın çıktısı; o program önce kendi başına, tam bir derleme adımıyla var edilmek zorunda.

Rust'ta üç ayrı prosedürel makro biçimi var: girdi olarak bir çağrı süsü alan biçim, bir
öznitelik satırının etkilediği öğeyi alan öznitelik makrosu ve türetme özniteliğinin
kendisini genişleten türetme makrosu. Üçü de aynı kısıtı taşıyor — üçü de kendi kasalarında
derlenmek zorunda — ve üçü de aynı türde bir işlev imzasını paylaşıyor: bir andaç akışı
girdi, bir andaç akışı çıktı. Öznitelik makrosu bir işlev ya da yapı bildiriminin üstüne
yazılan bir satırla tetiklenir ve altındaki öğeyi bir andaç akışı olarak alır; türetme makrosu
ise Türetilen Trait'ler dersinde görülen `#[derive(...)]` satırının kendisini genişletir ve
yalnız yeni bir gerçekleştirim üretebilir, üzerine yazıldığı öğeyi değiştiremez. Bu ders en
yalın biçimi, çağrı süslü olanı, ölçüyor; çünkü bu biçim girdisini ayrıştırmadan da sabit bir
çıktı üretebiliyor ve ölçümün odağını üretim mekaniğinden uzaklaştırmıyor.

#### Ölçüm: İki Aşamalı Derleme

Ölçüm program içinden kurulur. Bir Rust programı, `std::process::Command` ile derleyiciyi iki
kez çağırıyor: önce prosedürel makro kasasını `--crate-type proc-macro` ile derliyor, sonra
onu kullanan kaynağı `--extern` ile bu kasaya bağlayıp derliyor ve son olarak üretilen ikiliyi
çalıştırıp çıktısını bekleneni karşılaştırıyor. Kalıp, Rust'ın Tasarım Hedefleri dersinde
Go'nun Temelleri kursundan (M09/K01) alınan desenle aynı: derleyici bir alt süreç olarak
çağrılıyor, yalnız çıkış durumu ve çıktısı okunuyor; hata metni, yol, süre ve makine adı hiç
okunmuyor.

Ölçümün kendisi iki şeyi birden sağlıyor. Birincisi, ölçüm belirlenimci: iki kaynak da
programın içinde sabit dizgi olarak duruyor, hiçbiri dışarıdan okunmuyor, bu yüzden aynı
program her koşumda aynı iki kaynağı derliyor ve aynı üç satırı basıyor. İkincisi, ölçüm
kendi izini siliyor: çalışma dizini geçici bir klasörde açılıyor ve programın son satırı bu
klasörü tamamen kaldırıyor — ne kalıcı bir dosya bırakıyor ne de bir sonraki koşumu önceki
koşumun kalıntısından etkiliyor.

**MK4.** İki kasanın kaynak metni birer sabit dizgi olarak programın içinde tutuluyor. Kasa
kaynağı `extern crate proc_macro;` satırıyla başlıyor — bu, derleyicinin sysroot'unda hazır
duran, üçüncü taraf olmayan tek kasayı içe alıyor. Üretilen andaç akışı, elle andaç ağacı
kurmak yerine bir dize sabitinin ayrıştırılmasıyla (`.parse().unwrap()`) kuruluyor; bu, kasa
dışı hiçbir bağımlılık gerektirmeyen en doğrudan yol.

Gerçek prosedürel makrolar çoğunlukla girdi andaç akışını da işler — hangi adın, hangi
alanların geçtiğine bakıp çıktıyı ona göre biçimlendirir. Andaç akışını elle, andaç ağacı
kurarak ayrıştırmak, üçüncü taraf kasa kullanmadan da mümkündür, ama bu ders girdiyi hiç
okumuyor: `_girdi` parametresi kullanılmadan bırakılıyor, çünkü ölçülen şey andaç işleme
becerisi değil, üretimin **hangi derleme adımında** gerçekleştiği. Girdiyi okuyup ona göre
dallanan bir makro da aynı iki aşamalı derlemeye tabi olurdu; girdiyi okumak üretimin yerini
değiştirmez, yalnız üretilen metnin içeriğini değiştirir.

```rust
// olusum.rs — bir prosedurel makro kasasi program icinden derlenir, sonra
// onu kullanan kaynak ikinci bir cagriyla derlenip kosturulur.
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

const KASA_KAYNAK: &str = r#"
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn govde_uret(_girdi: TokenStream) -> TokenStream {
    "fn uretilen_deger() -> i32 { 42 }".parse().unwrap()
}
"#;

const TUKETEN_KAYNAK: &str = r#"
makro_kasa::govde_uret!();

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

fn main() {
    let dogrudan: i32 = uretilen_deger();
    let genel_sinir_icinde: i32 = uygula(uretilen_deger);
    println!("uretilen_deger={} genel_sinir_icinde={}", dogrudan, genel_sinir_icinde);
}
"#;

const BEKLENEN_CIKTI: &str = "uretilen_deger=42 genel_sinir_icinde=42\n";

fn calisir_mi(komut: &mut Command) -> bool {
    komut.output().map(|c| c.status.success()).unwrap_or(false)
}

fn siniflandir(basarili: bool) -> &'static str {
    if basarili { "derlendi" } else { "derlenmedi" }
}

fn main() {
    let calisma = env::temp_dir().join(format!("prosedurel-{}", std::process::id()));
    fs::create_dir_all(&calisma).unwrap();

    let kasa_yolu = calisma.join("kasa.rs");
    fs::write(&kasa_yolu, KASA_KAYNAK).unwrap();

    let kasa_ikili: PathBuf = calisma.join(format!("libmakro_kasa.{}", env::consts::DLL_EXTENSION));
    let kasa_derlendi = calisir_mi(
        Command::new("rustc")
            .args(["--edition", "2021", "--crate-type", "proc-macro", "--crate-name", "makro_kasa"])
            .arg("-o").arg(&kasa_ikili)
            .arg(&kasa_yolu),
    );
    println!("kasa: {}", siniflandir(kasa_derlendi));

    let tuketen_yolu = calisma.join("tuketen.rs");
    fs::write(&tuketen_yolu, TUKETEN_KAYNAK).unwrap();
    let tuketen_ikili = calisma.join("tuketen");

    let tuketen_derlendi = kasa_derlendi
        && calisir_mi(
            Command::new("rustc")
                .args(["--edition", "2021"])
                .arg("--extern").arg(format!("makro_kasa={}", kasa_ikili.display()))
                .arg("-o").arg(&tuketen_ikili)
                .arg(&tuketen_yolu),
        );
    println!("tuketen: {}", siniflandir(tuketen_derlendi));

    let eslesti = tuketen_derlendi
        && Command::new(&tuketen_ikili)
            .output()
            .map(|c| c.stdout == BEKLENEN_CIKTI.as_bytes())
            .unwrap_or(false);
    println!("cikti eslesti: {}", eslesti);

    let _ = fs::remove_dir_all(&calisma);
}
```

```
kasa: derlendi
tuketen: derlendi
cikti eslesti: true
```

**MK5.** İkili dosyanın uzantısı hiçbir yerde sabit yazılmıyor; `env::consts::DLL_EXTENSION`
platformun kendi paylaşımlı kütüphane uzantısını veriyor ve program bunu okuyup dosya adına
katıyor. Bu, gerçek bir makine adı ya da dizin yolu basmadan taşınabilir bir örnek kurmanın
yolu: uzantı ekrana hiç yazılmıyor, yalnız dosya adının içinde, görünmeden kullanılıyor.
`--extern makro_kasa=<yol>` bağı, ilk derlemenin ürettiği ikiliyi ikinci derlemeye bir kasa
adıyla tanıtıyor — tüketen kaynak, üretim makrosunun çağrısını tıpkı standart kitaplığın
herhangi bir kasasını kullanır gibi, kasa adını önüne koyarak yazıyor.

Bu bağlama adımı, bir paket kaydı ya da bir bağımlılık çözümleyici gerektirmiyor; `--extern`
yalnız bir ad ile bir dosya yolunu eşliyor ve derleyici o yoldaki ikiliyi doğrudan okuyor.
Ölçüm bu yüzden bir ağ bağlantısı ya da bir dış kayıt defteri açmadan, yalnız iki `rustc`
çağrısı ve aralarında geçen bir dosya yoluyla kuruluyor — kursun üçüncü taraf kasa
kullanmama kuralı burada da bozulmuyor: bağlanan tek kasa, programın kendi ürettiği
`makro_kasa`.

Sonuç üç satır: kasa derlendi, tüketen kaynak derlendi, çıktı bekleneni karşıladı. Üçü de
sınıflandırıcı sonuçlar — hiçbiri hata metni, süre ya da yol taşımıyor. `tuketen` kaynağı
üretilen işlevi iki ayrı biçimde kullanıyor: bir kez doğrudan çağrıyla, bir kez de `uygula`
işlevinin `F: Fn() -> i32` sınırının içinden. İkisi de aynı sonucu (`42`) veriyor ve ikisi de
derleniyor — üretilen gövde, önceki derste görüldüğü gibi burada da bir genel sınırın içinde
sıradan bir işlevmiş gibi denetleniyor. Fark yalnız üretimin **nerede** olduğunda: önceki
derste üretim aynı okumanın içindeydi, burada üretim çoktan bitmiş, derlenmiş bir ikili olarak
ikinci derlemeye giriyor.

**MK6.** `tuketen` kaynağının genel sınırı sınayan ikinci satırı tesadüfen eklenmiş bir
süsleme değil: bu ders, önceki dersteki "sürdürüyor" iddiasının yalnız o kursun tek bir
kurgusuna özgü olmadığını, üretim ayrı bir derleme adımına taşındığında da geçerli kaldığını
göstermek için bilerek aynı sınamayı tekrarlıyor. İki ders farklı üretim mekanizmaları
kullanıyor — biri metinsel yer değiştirme, öbürü ayrı bir programın çalıştırılması — ama
ikisinin de ürettiği kaynak, derleyicinin gözünde aynı statüde: sıradan Rust kaynağı.

Bu benzerliğin bir sınırı var. Go'nun Tasarım Amaçları dersinde (M09/K01) kurulan `derlenirMi`
işlevi, tek bir kaynağı tek bir derleyici çağrısıyla sınıyordu; burada iki kaynak ve iki
sıralı çağrı gerekiyor, çünkü ikinci çağrı birincinin çıktısına bağımlı. `tuketen_derlendi`
değişkeninin `kasa_derlendi &&` ile başlaması bu bağımlılığı kodun içinde görünür kılıyor:
kasa derlenmediyse tüketen kaynağı derlemeye çalışmanın hiçbir anlamı yok, çünkü bağlanacak
bir ikili yok. Aynı mantık `eslesti` değişkeninde bir kez daha tekrarlanıyor — tüketen kaynak
derlenmediyse çalıştırılacak bir ikili de yok. Bu zincirleme kısa devre, kodun kendisinde
üretimin sırasını da belgeliyor: bir adım başarısız olduğunda sonraki adımlar hiç
denenmiyor ve son satırdaki `false` değeri, hangi adımda durulduğunu ayırt etmeden yalnız
"zincir tamamlanmadı" diyor.

Türetilen Trait'ler dersi (M10/K03), bir öznitelik satırının kaynakta hiç gövdesi olmayan bir
gerçekleştirim doğurduğunu ve bu gövdenin kaç kez koştuğunun sayılamadığını göstermişti. Bu
ders o sayı iddiasını tekrarlamıyor: burada ölçülen kaç kez koştuğu değil, üretimin hangi
derleme adımında gerçekleştiği ve o adımın gerçekten geçip geçmediği. İki ders farklı
sorular soruyor ama aynı gözlemi paylaşıyor: kaynakta elle yazılmamış bir gövde de, derleyici
açısından sıradan bir gövdeden ayırt edilmiyor.

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

Prosedürel makro kendi kasasında yaşamak zorundadır ve bu bir dil kısıtıdır — bir tercih değil.
Kasa kaynağını (`KASA_KAYNAK`) ve tüketen kaynağı (`TUKETEN_KAYNAK`) tek bir dosyada, tek bir
derleme çağrısıyla derlemeye çalışan bir yazım derleyici tarafından reddedilir: `proc-macro`
kasa türü yalnız makro tanımları dışa açabilir ve bir kasa kendi tanımladığı prosedürel
makroyu kendi içinde çağıramaz. Bu ret düzyazıda anlatılıyor, kod bloğuna konmuyor, çünkü
kursun genel kuralı gereği derlenmeyen bir kaynak hiçbir zaman kod bloğuna girmiyor.

Bu kısıt, ölçümün kurgusunu doğrudan belirledi. Önceki derste tek bir `rustc` çağrısı
yetiyordu, çünkü makro tanımı ve çağrısı aynı okumanın parçasıydı. Burada iki ayrı `rustc`
çağrısı gerekiyor, çünkü kasa önce **var olmak** zorunda — ikinci çağrı, birincinin ürettiği
ikiliyi bir girdi olarak alıyor. Prosedürel makronun "sürdürüyor" sınıfına düşmesi bu yüzden
aynı zamanda dilin bir mimari kararını da açığa çıkarıyor: derleyicinin genişletme sırasında
çalıştırdığı kod, derlenmekte olan kaynağın kendisinden **önce** ve **ayrı** var olmalı.
Üretilen gövdenin denetimi değişmiyor, ama o denetimin ne zaman ve kaç aşamada gerçekleştiği
değişiyor.

Kısıtın ikinci yüzü, kasanın **neyi dışa açabileceğinde**. `proc-macro` kasa türüyle derlenen
bir kasa, yalnız prosedürel makro tanımlarını dışa açabilir; sıradan bir `pub fn` ya da bir
`pub struct` aynı kasadan dışa açılamaz. Bu, önceki paragraftaki reddin bir uzantısı: kasa
türü kasanın **rolünü** belirliyor ve bu rol yalnız "makro üretimi" ile sınırlı. Bir proje
hem sıradan bir işlev hem bir prosedürel makro sunmak istiyorsa, ikisini iki ayrı kasaya
bölmek zorunda — biri `proc-macro` türünde, öbürü sıradan bir kitaplık kasası. Bu ayrım
ölçümün kurgusunda da izleniyor: `kasa.rs`, üretim makrosu dışında hiçbir şey dışa açmıyor.

Bu kısıtın bir bedeli daha var: iki kasalı bir yapı, tek kasalı bir yapıdan her zaman daha
fazla derleme adımı gerektiriyor. Önceki derste bir değişiklik yapıldığında derleyici tek bir
dosyayı okuyup tek bir ikili üretiyordu; burada kasa kaynağında yapılan en küçük bir değişiklik
bile, önce kasanın yeniden derlenmesini, sonra tüketen kaynağın o yeni ikiliyle yeniden
derlenmesini gerektiriyor. Bu, üretimin programlanabilir olmasının getirdiği gücün karşılığı:
metinsel bir yer değiştirmenin ötesine geçen her üretim biçimi, kendi ayrı derleme
zaman çizelgesini de beraberinde getiriyor.

#### Özet

- Prosedürel makro, girdi andaç akışını işleyip çıktı andaç akışı döndüren sıradan bir Rust
  işlevidir; bu işlev kendi `proc-macro` kasasında derlenmek zorundadır ve bu kasadan yalnız
  makro tanımları dışa açılabilir.
- Ölçüm program içinden kuruldu: derleyici iki kez, sırayla çağrıldı, yalnız derlendi/
  derlenmedi ve çıktı eşleşmesi basıldı — hata metni, yol, süre ve makine adı hiç okunmadı.
- Üretilen gövde bu kurguda da sürdürüyor sınıfına düşüyor: hem doğrudan çağrıldığında hem de
  bir genel sınırın içinde kullanıldığında sıradan kaynak gibi denetleniyor; fark üretimin
  aynı okumada değil, ayrı bir derleme adımında gerçekleşmesinde.
- Prosedürel makronun kendi kasasında yaşamak zorunda olması bir tercih değil, dilin kısıtı:
  bu yüzden ölçüm tek bir derleme yerine iki ayrı, sıralı derleme çağrısı gerektirdi.
- Bu kısıt bir bedel taşıyor: kasa kaynağındaki en küçük değişiklik bile, hem kasanın hem onu
  kullanan kaynağın yeniden derlenmesini gerektiriyor.

#### Sonraki Adım

Bu iki ders, üretilen kaynağın nasıl denetlendiğini iki farklı kurguda gösterdi: bir kez aynı
okumada metinsel yer değiştirmeyle, bir kez ayrı bir derleme adımında çalıştırılan bir
programla. İkisinde de ortak bir soru henüz sorulmadı — üretim neden tercih ediliyor, elle
yazmak yerine. Sıradaki ve bu konunun son dersi bu soruyu ele alıyor: aynı yapı bir makro
yazımıyla ve elle kurulmuş bir yazımla ayrı ayrı oluşturuluyor, aynı sonucu verdiği
gösteriliyor ve makro yazımının aynı işi kaç satır kısalttığı sayılıyor. Ardından bu
kısalmanın bir doğruluk garantisi getirmediği, yalnız bir okunabilirlik kazancı olduğu bir
örnekle gösteriliyor.

### Alan Özel Diller

Source: https://academia.sh/tr/kurslar/rust-ileri/alan-ozel-diller

Aynı yönlendirme yapısı bir makro yazımıyla ve elle kurulan yazımla ayrı ayrı oluşturulur, aynı sonucu verdiği koşturularak gösterilir ve makro yazımının kaynağı kaç satır kısalttığı sayılır; ama makro çıktısının okunaklı olması doğruluk getirmez — mantıksal olarak yanlış bir yol-işlev bağı da aynı kolaylıkla tip denetiminden geçer.

Önceki iki ders, üretilen bir gövdenin nasıl denetlendiğini iki farklı kurguda gösterdi: bir
kez aynı okumada metinsel yer değiştirmeyle, bir kez ayrı bir derleme adımında çalıştırılan
bir programla. İkisinde de ortak bir soru henüz sorulmadı: üretim neden tercih ediliyor, elle
yazmak yerine? Bu konunun son dersi bu soruyu ele alıyor. Bir makro, çağıranın gözünden bir
küçük dil gibi görünen bir arayüz kurabilir — kendi sözdizimiyle, kendi kısayollarıyla; buna
alan özel dil deniyor. Bu ders aynı yapıyı iki yazımla kuruyor, ikisinin aynı sonucu
verdiğini koşturarak doğruluyor, makro yazımının kaç satır kısalttığını sayıyor ve sonunda bu
kısalmanın bir doğruluk garantisi taşımadığını gösteriyor.

Alan özel dil terimi burada geniş bir çerçeveyi değil, dar ve somut bir kullanımı işaret
ediyor: kaynağı okuyan tarafın, altında yatan `match` bloğunu, işlev tanımını ya da başka bir
mekaniği hiç görmeden, doğrudan problem alanının kelimeleriyle (bir yol, bir işleyici) yazı
yazabilmesi. Bu arayüz derleyicinin yeni bir kural öğrenmesini gerektirmiyor — makro
genişledikten sonra ortaya çıkan kaynak, önceki iki derste görüldüğü gibi, sıradan Rust
kaynağından ayırt edilemiyor. Kazanılan şey yalnız kaynağı **yazma** biçimi; kaynağı
**denetleme** biçimi hiç değişmiyor.

Bu tanım, alan özel dil terimini programlama dilleri kuramındaki geniş kullanımından
ayırıyor: orada terim çoğunlukla kendi ayrıştırıcısı, kendi çalışma zamanı olan bağımsız bir
dili işaret ediyor. Burada ise ortada ikinci bir dil yok — tek bir dil var, Rust'ın kendisi
ve makro yalnız o dilin sözdizimini belirli bir problem için daha kısa bir kalıba
sığdırıyor. Bu yüzden derste kurulan arayüz, kendi başına bir ayrıştırıcı taşımıyor;
ayrıştırma zaten derleyicinin kendi ayrıştırıcısı tarafından, makro genişlemesinin bir
parçası olarak yapılıyor.

#### Aynı Yapı, İki Yazım

Bir yönlendirme tablosu — bir yol dizgisini bir işleve bağlayan bir tablo — hem bir makro
yazımıyla hem elle kurulabilir. Makro yazımı, yol ve işleyici çiftlerini bir liste gibi
sıralar; elle yazım aynı tabloyu bir `match` bloğunun dallarına döker. İkisi de aynı işi
yapıyor: bir yol dizgisini bir sonuca çeviriyor.

Elle yazılan biçim her yeni yol için üç şeyi tekrar etmeyi gerektiriyor: `match` bloğunun
kendisini, bir dalın sözdizimini ve yakalayıcı dalın varlığını. Bu tekrar bir hata
değil — Desen Eşleme dersinde (M10/K01) görüldüğü gibi, `match`'in bütünlük istemesi kaynağın
güvenli kalmasını sağlıyor. Ama tekrar eden kısım, yol ile işleyici çiftinden başka hiçbir
yeni bilgi taşımıyor: `match`, `yol {`, `_ => -1` satırları her yönlendirme tablosunda
birebir aynı. Makro bu sabit kalıbı bir kez, kendi tanımının içinde yazıyor ve her çağrıda
yeniden üretiyor; çağıran taraf yalnız değişen kısmı — çiftlerin kendisini — yazıyor.

```rust
// yonlendirici.rs — ayni yonlendirme tablosu bir makro yazimiyla ve elle kurulan esdegeriyle olusturuluyor.
macro_rules! yonlendirici_olustur {
    ($ad:ident { $($yol:literal => $isleyici:expr),+ $(,)? }) => {
        fn $ad(yol: &str) -> i32 {
            match yol {
                $($yol => $isleyici,)+
                _ => -1,
            }
        }
    };
}

fn ana_sayfa() -> i32 { 200 }
fn hakkinda_sayfa() -> i32 { 200 }

yonlendirici_olustur!(makro_ile_yonlendir {
    "/ana" => ana_sayfa(),
    "/hakkinda" => hakkinda_sayfa(),
});

fn elle_yonlendir(yol: &str) -> i32 {
    match yol {
        "/ana" => ana_sayfa(),
        "/hakkinda" => hakkinda_sayfa(),
        _ => -1,
    }
}

fn main() {
    for yol in ["/ana", "/hakkinda", "/bilinmeyen"] {
        let m = makro_ile_yonlendir(yol);
        let e = elle_yonlendir(yol);
        println!("{:<12}makro={} elle={} ayni_mi={}", yol, m, e, m == e);
    }
}
```

```
/ana        makro=200 elle=200 ayni_mi=true
/hakkinda   makro=200 elle=200 ayni_mi=true
/bilinmeyen makro=-1 elle=-1 ayni_mi=true
```

**MK7.** Makro tanımının deseni üç parçadan kuruluyor: `$ad:ident` üretilecek işlevin adını
yakalıyor, `$yol:literal => $isleyici:expr` çiftini bir ya da daha çok kez (`+`) tekrarlıyor,
`$(,)?` de son çiftten sonra isteğe bağlı bir virgülü kabul ediyor. Bu üç parça birlikte, dilin
kendi sözdizimini bir liste-benzeri yazıma dönüştürüyor: çağıran taraf `match` yazmıyor, süslü
parantez içine yol-işlev çiftlerini art arda diziyor. Üç yoldan (`/ana`, `/hakkinda`,
`/bilinmeyen`) üçü de iki yazımda birebir aynı sonucu veriyor; `ayni_mi` sütunu bunu tek tek
doğruluyor.

Bu ifade edici arayüz bildirimsel bir makroyla kuruldu, prosedürel bir makroyla değil — bu
seçim rastgele değil. Önceki dersin gösterdiği gibi, prosedürel bir makro ayrı bir kasada
derlenmek zorunda ve onu kullanmak iki aşamalı bir derleme gerektiriyor. Burada kurulan
yönlendirme tablosu, tekrar eden bir sözdizim kalıbından ibaret; bu tür bir tekrarı sarmak
için andaç akışını elle işlemeye, ayrı bir kasaya ya da iki aşamalı bir derlemeye gerek yok —
bildirimsel makronun desen eşleme düzeneği zaten yeterli. Prosedürel makro, girdi
üzerinde gerçek bir hesaplama yapmak gerektiğinde tercih ediliyor; yalnız bir kalıbı
tekrarlamak için değil.

Üretilen `makro_ile_yonlendir` işlevi ile elle yazılan `elle_yonlendir` işlevi, derleyicinin
gözünde aynı statüde: ikisi de birer `fn`, ikisi de aynı imzayı taşıyor, ikisi de aynı `match`
mantığını çalıştırıyor. Bu, önceki iki dersin bulgusunu bir kez daha doğruluyor —
üretilen gövde "sürdürüyor" sınıfına düşüyor — ama bu derste asıl soru gövdenin denetlenip
denetlenmediği değil, gövdeyi **yazmanın** ne kadar kısaldığı.

Bu eşitliğin bir sonucu var: iki yazım arasında seçim yaparken, çalışma zamanı davranışı
hiçbir zaman bir ölçüt olamaz — ikisi de birebir aynı şeyi üretiyor. Seçimi belirleyen tek
eksen kaynağın nasıl **okunduğu** ve nasıl **yazıldığı**. Yol sayısı arttıkça bu fark daha
belirgin hale geliyor: üç yol yerine otuz yol eklemek gerekseydi, elle yazım otuz kez `match`
dalının sözdizimini tekrar ederdi, makro yazımı yalnız otuz çift eklerdi. Bu ders bu farkı
sezgisel bırakmıyor, sayıyla ölçüyor.

#### Kaynağın Kendi Uzunluğu

Makronun gerçekte kaç satır ürettiğini çalışma zamanında ölçmenin doğrudan bir yolu yok —
genişleme derleme sırasında olur ve çalışan programın eline yalnız sonuç geçer. Ölçülebilir
olan, iki yazımın **kaynaktaki** uzunluğu: makro çağrısının kendisi kaç satır, aynı işi yapan
elle yazılmış `match` bloğu kaç satır. Bu iki kaynak metni birer dize sabiti olarak tutulup
satır sayıları karşılaştırılabilir.

Bu, bir önceki bölümdeki koşumdan bağımsız, ayrı bir ölçüm. Önceki bölüm iki yazımın **aynı
sonucu** verdiğini gösterdi; bu bölüm ikisinin **aynı işi ne kadar kaynakla** yaptığını
gösteriyor. İkisi birbirini tamamlıyor: birincisi olmadan ikincisi anlamsız kalırdı — kısa
ama yanlış bir yazımı kısa olduğu için övmenin bir anlamı yok. Bu yüzden bu ders satır
sayımına geçmeden önce eşitliği koşturarak doğruladı; sıra bilinçli seçildi, tersi olsaydı
kısalığın doğruluğun yerine geçtiği izlenimi doğardı.

```rust
// satir_sayimi.rs — ayni yonlendirme tanimi iki yazimda tutuluyor, satir sayisi karsilastiriliyor.
fn main() {
    let makro_yazimi = "yonlendirici_olustur!(makro_ile_yonlendir {\n    \"/ana\" => ana_sayfa(),\n    \"/hakkinda\" => hakkinda_sayfa(),\n});";
    let elle_yazim = "fn elle_yonlendir(yol: &str) -> i32 {\n    match yol {\n        \"/ana\" => ana_sayfa(),\n        \"/hakkinda\" => hakkinda_sayfa(),\n        _ => -1,\n    }\n}";

    let makro_satir = makro_yazimi.lines().count();
    let elle_satir = elle_yazim.lines().count();
    println!("makro yazimi: {} satir", makro_satir);
    println!("elle yazim: {} satir", elle_satir);
    println!("kisalan satir: {}", elle_satir - makro_satir);
}
```

```
makro yazimi: 4 satir
elle yazim: 7 satir
kisalan satir: 3
```

**MK8.** Bu ölçüm, önceki derste `--extern` yolunun neden ekrana basılmadığını hatırlatan
aynı gerekçeyle kuruldu: makronun genişlemesini çalışma zamanında okumanın bir yolu yok, o
yüzden ölçüm çalışma zamanına taşınabilecek tek şeyi — kaynağın kendi metnini — ölçüyor. Rust'ın
Tasarım Hedefleri dersinde (M10/K01) örtük bir tür dönüşümünün reddi de aynı yöntemle, iki
kaynak satırının karakter sayısı karşılaştırılarak ölçülmüştü; burada birim karakter değil
satır, ama yöntem aynı: kaynağı çalıştırmak yerine kaynağın kendisini bir veri olarak ele
almak.

Elle yazılan yönlendirme yedi satır: işlev imzası, `match` başlığı, iki dal, yakalayıcı dal,
iki kapanış parantezi. Makro çağrısı dört satır: çağrının kendisi, iki yol-işlev çifti,
kapanış. Aradaki üç satırlık fark, `fn`, `match` ve kapanış parantezlerinin makro tarafında hiç
yazılmasına gerek kalmamasından geliyor — bu üç satır, makro tanımının içinde **bir kez**
yazılı ve her çağrıda yeniden üretiliyor. Yol sayısı arttıkça bu oran değişmiyor: her yeni
yol-işlev çifti, iki yazımda da birer satır ekliyor, ama iki yazım arasındaki sabit üç satırlık
fark hep aynı kalıyor.

Bu sabit maliyet, ölçümün neden "kısalan satır" diye mutlak bir sayı bastığını, "kaç kat kısa"
diye bir oran basmadığını da açıklıyor. İki yol için fark üç satır, otuz yol için de fark yine
üç satır olurdu — oran küçülür, mutlak fark değişmez. Ölçüm bu yüzden oranı değil farkı
basıyor: kursun sayı kuralı gereği bir iddia yalnız gerçekten ölçülen büyüklüğü taşımalı ve
burada belirlenimci olarak ölçülebilen şey iki sabit kaynağın satır farkı, bir genelleme
değil.

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

Okunaklı bir yazım doğruluk getirmiyor. Makro, çağıranın yazdığı yol-işlev çiftlerinin
**anlamlı** olup olmadığını hiç sormuyor — yalnız her çiftin bir dizgi sabiti ve `i32`
döndüren bir ifade olup olmadığına bakıyor. Bu sınır önceki iki bölümdeki ölçümlerin
üzerine ekleniyor, onları geçersiz kılmıyor: makro yazımının aynı sonucu verdiği ve kaynağı
kısalttığı doğru kalıyor; ama bu iki kazanç, üçüncü bir soruya — kurulan tablonun doğru
olup olmadığına — hiçbir yanıt taşımıyor. Aşağıdaki örnek, bir yolun yanlışlıkla olması
gerekenden başka bir işleve bağlandığı bir tabloyu aynı makroyla kuruyor.

```rust
// yanlis_ama_dogru_tip.rs — mantiksal olarak yanlis bir esleme de tip denetiminden geciyor.
macro_rules! yonlendirici_olustur {
    ($ad:ident { $($yol:literal => $isleyici:expr),+ $(,)? }) => {
        fn $ad(yol: &str) -> i32 {
            match yol {
                $($yol => $isleyici,)+
                _ => -1,
            }
        }
    };
}

fn basarili() -> i32 { 200 }

yonlendirici_olustur!(hatali_yonlendir {
    "/ana" => basarili(),
    "/hata" => basarili(),
});

fn main() {
    println!("/hata -> {}", hatali_yonlendir("/hata"));
}
```

```
/hata -> 200
```

**MK9.** `/hata` yolu, adından da anlaşılacağı gibi bir başarısızlık durumunu temsil etmesi
beklenen bir yol; ama tabloyu kuran satır yanlışlıkla `basarili()` işlevine bağlanmış ve
sonuç `200`. Makro bunu reddetmiyor, çünkü reddedecek bir bilgisi yok: `$isleyici:expr`
deseni yalnız "burada `i32` döndüren bir ifade olacak" diyor, o ifadenin çağrıldığı yolun
anlamıyla tutarlı olup olmadığını sormuyor. Derleyici de aynı nedenle sessiz kalıyor —
`basarili()` gerçekten `i32` döndürüyor, imza tutuyor, tür denetimi geçiyor. Üretilen gövde
yine "sürdürüyor" sınıfına düşüyor, çünkü tür denetimi gerçekten çalışıyor; ama tür denetimi
burada "bu doğru mu" sorusuna hiç yanıt vermiyor, yalnız "bu türden mi" sorusuna yanıt
veriyor.

Bu örnekte hata elle de aynı kolaylıkla yazılabilirdi — `elle_yonlendir` içindeki bir
dalı yanlış işleve bağlamak da aynı şekilde derlenirdi. Makro burada hatayı **kolaylaştırmıyor**;
yalnız hatayı **gizlemiyor** da. Fark, iki yazımın da aynı zayıflığı taşıdığı ama makro
yazımının bu zayıflığı daha az satırda ve daha az yazımla ulaşılır kıldığı: yanlış bir çift
eklemek, otuz satırlık bir `match` bloğunda bir dalı değiştirmekten daha az dikkat gerektiren
bir işlem. Kaynağın kısalması, kaynağın gözden geçirilmesini kolaylaştırıyor ama gözden
geçirmenin yerini almıyor.

Bu gözlem, kursun dört sınıfından hiçbirine yeni bir beşinciyi eklemiyor — "yanlış ama tür
uyumlu" ayrı bir sınıf değil, "sürdürüyor" sınıfının kendi içindeki bir ayrıntı. Derleyici
burada da gerçekten denetliyor, hiçbir adımı atlamıyor; yalnız denetlediği şey, kaynağı
okuyan bir insanın sorduğu sorudan daha dar: "bu ifade beklenen türde mi", "bu bağ doğru
mu" değil.

Bu, önceki iki dersteki gözlemlerle aynı sınıfta duran ama farklı bir köşeden gösterilen bir
sınır. Bildirimsel Makrolar dersi, aynı makronun `i32` ve `bool` gibi iki anlamca uzak türle
çağrılabildiğini, ikisinin de derlenip makronun hangisinin anlamlı olduğunu söyleyemediğini
göstermişti. Burada tür sabit (`i32`), değişen şey bir dizginin **hangi ifadeyle**
bağlandığı — ama sonuç aynı: makro sözdizimi düzeyinde çalışıyor, iki farklı bağın
birbirinden **anlamca** ayrıldığını göremiyor, yalnız ikisinin de biçimce doğru olduğunu
görüyor. Okunaklı bir yazım, yalnız yazan tarafın niyetini daha kısa ifade etmesini
sağlıyor; o niyetin doğru kurulup kurulmadığını hâlâ okuyan tarafın kontrol etmesi
gerekiyor.

Prosedürel Makrolar dersinde görülen ayrım da burada geçerliliğini koruyor: bir prosedürel
makro, girdi andacını okuyup üzerinde daha karmaşık bir işlem yapabilseydi bile, yine yalnız
**tür uygunluğuna** bakardı — çünkü Rust'ta tür denetimi her zaman genişlemeden sonra,
üretilen kaynak üzerinde çalışıyor, hangi makro biçimi kullanılırsa kullanılsın. İki makro
biçiminin (bildirimsel, prosedürel) üretim mekanizması farklı olsa da, ikisinin de vardığı
kaynak aynı denetim sürecinden geçiyor ve o denetim sürecinin sınırı da ikisinde aynı: tür
doğru mu, anlam doğru mu sorularının yalnız ilkine yanıt veriyor.

#### Özet

- Aynı yönlendirme yapısı bir makro yazımıyla ve elle kurulan yazımla oluşturuldu; üç yol için
  ikisi de birebir aynı sonucu verdi ve bu eşitlik koşturularak, tek tek karşılaştırılarak
  doğrulandı.
- Üretilen gövde bu derste de sürdürüyor sınıfına düşüyor: makro çağrısının ürettiği işlev,
  elle yazılmış işlevle aynı kurallarla denetleniyor.
- Makro yazımı aynı işi üç satır kısalttı; bu kısalma makronun sabit başlık-kapanış
  maliyetini bir kez yazıp her çağrıda yeniden kullanmasından geliyor ve yol sayısı arttıkça
  mutlak farkın kendisi değişmiyor.
- Okunaklı bir makro yazımı doğruluk garantisi getirmiyor: mantıksal olarak yanlış bir
  yol-işlev bağı da, türü tuttuğu için aynı kolaylıkla derleniyor.
- Makronun denetleyebildiği tek şey biçim — bir ifadenin beklenen türden olup olmadığı; o
  ifadenin çağıranın niyetiyle örtüşüp örtüşmediğini denetleyen bir mekanizma yok.
- İfade edici bir arayüz kurmak için bildirimsel makro seçildi; prosedürel makro yalnız girdi
  üzerinde gerçek bir hesaplama gerektiğinde tercih ediliyor, sabit bir kalıbı sarmak için
  değil.

#### Sonraki Adım

Bu konunun üç dersi, üretilen kaynağın üç ayrı kurguda — aynı dosyada, ayrı bir derleme
adımında, ifade edici bir arayüzün içinde — hep aynı sınıfa, sürdürüyor sınıfına düştüğünü
gösterdi. Üçünde de ortak kalan sınır aynıydı: derleyici üretilen kaynağın **türünü**
denetliyor, **anlamını** denetlemiyor. Sıradaki konu, Düşük Seviye ve Birlikte Çalışma, bu
sınırı derinleştiriyor: derleyicinin denetimi hiç durmadığı bu üç dersin ardından, sıradaki
ders denetimin **gerçekten** durduğu bir bloğa giriyor — orada da denetim toptan kapanmıyor,
yalnız birkaç işlem "devredildi" sınıfına açılıyor ve geri kalan her şey, tıpkı burada olduğu
gibi, sürmeye devam ediyor.

## Düşük Seviye ve Birlikte Çalışma

Derleyicinin belirli işlemlerde denetimi yazana devretmesi ve bunun sözleşmesi, başka bir dilin işlevlerinin yalnız imzasına güvenilerek çağrılması, tarayıcıda koşan bir hedefin getirdiği kısıtlar, ve standart kütüphanesiz ortamların neyi elden aldığı.

### `unsafe` Rust

Source: https://academia.sh/tr/kurslar/rust-ileri/unsafe-rust

unsafe bloğunun derleyicinin bütün denetimini değil, yalnız birkaç belirli işlemi kapsayan denetimini kapattığı; aynı blok içinde bir ham işaretçi okumasının denetimsiz kaldığı ama bir dilim sorgusunun hâlâ denetlendiği koşturularak gösterilir.

Önceki konu, bir makronun ürettiği gövdenin biçimi ne kadar farklı görünürse görünsün
yine sıradan kaynak gibi tip denetiminden geçtiğini gösterdi: derleyici üretilen bir
işlevi de, elle yazılmış bir işlev kadar sıkı denetledi. Bu konu aynı soruyu ters yönden
sorar: derleyicinin bu denetimi hiç yapmadığı, yapmayı bırakıp yazan tarafa bıraktığı bir
yazım var mıdır? Yanıt `unsafe` anahtar sözcüğüdür — ama bu dersin bulgusu, o sözcüğün
göründüğü kadar geniş bir kapı açmadığıdır.

Ödünç Denetleyicisi dersi (M10/K02) derleyicinin sahiplik ve ödünç kurallarını nasıl
sınadığını, hangi yazımları reddettiğini kurdu. Bu ders o denetleyicinin **kapandığını**
göstermez — kapanmadığını, yalnızca birkaç işlemin önündeki engelin kaldırıldığını
gösterir. Standart Kütüphane ve Akışlar dersi (M08/K03) bir davranışın garantisini kimin
verdiğini sormuştu: arayüz mü, gerçekleştirim mi, çağıran mı — bir sözleşmenin tarafları.
Bu derste sorulan farklıdır: derleyicinin kendi denetiminin, sözleşmenin hiçbir tarafına
bakmaksızın, nerede durduğu.

Bu kapının var olmasının bir nedeni var: ödünç denetleyicisinin sınadığı kurallar,
gerçekleştirilebilecek her doğru programı kapsamıyor. Bir dairesel tampon, bir düğüm
havuzu ya da bir donanım kaydına doğrudan erişen bir sürücü gibi düzenekler, ödünç
denetleyicisinin izleyebileceğinden daha karmaşık bir sahiplik örüntüsü taşıyabilir —
denetleyici bu örüntüyü doğru bulsa bile reddedebilir, çünkü sınama kuralı örüntünün
kendisinden değil, denetleyicinin izleyebildiği örüntü sınıfından gelir. `unsafe`, bu
uyuşmazlığı kapatan bir kaçış menfezidir: yazan taraf, denetleyicinin sınayamadığı bir
gerekçeyle "bu doğru" diyebilir. Standart kütüphanenin kendi büyüyen dizi ve dilim
tipleri de böyle kurulmuştur — dışarıdan bakan koda güvenli bir arayüz sunarlarken,
kendi gövdelerinde aynı ham işaretçi işlemlerini kullanırlar. Bu ders o gövdelerin
içine bakmaz; yalnızca aynı iznin sıradan bir programda nasıl çalıştığını ölçer.

#### `unsafe` Bloğunun Sınırlı İzni

Bir ham işaretçi kurmak `unsafe` gerektirmez. `&deger as *const i32` yazımı sıradan bir
ifadedir, derlenir ve hiçbir denetimi atlamaz — kurulan şey yalnız bir adrestir, henüz
okunmamıştır. Ödünç kuralları da bu satırda devrede değildir, çünkü ham işaretçiler ödünç
denetleyicisinin izlediği tipler arasında yer almaz; onlar için sahiplik, yaşam süresi ve
tekillik gibi bir muhasebe tutulmaz.

Kısıt, işaretçiyi **okumaya** çalışıldığında başlar. Bir ham işaretçinin gösterdiği yerde
gerçekten geçerli bir değer olduğunu derleyici bilemez — işaretçi boş olabilir, ait olduğu
bellek geri verilmiş olabilir, ya da yalnızca uydurma bir adres taşıyor olabilir. Derleyici
bu üç olasılığı ayırt edemediği için, okuma işlemini `unsafe` bloğunun içine almayı zorunlu
kılar: kod derlenir, ama "burada bir sözleşme var ve onu ben karşılıyorum" beyanı yazan
tarafa aittir. Aynı kısıt, gövdesi `unsafe` işaretli bir işlevi çağırmak için de geçerlidir
— işlevin adı, çağıranın bir ön koşulu sağlaması gerektiğini söyler, ama bu koşulu
derleyici sınamaz.

Bu izin listesi kısadır ve dilin kendisi tarafından sabitlenmiştir: derleyici hangi
işlemlerin `unsafe` gerektirdiğini kaynaktan okur, çağıranın niyetinden değil. Aşağıdaki
koşum, `unsafe` gerektirmeyen üç işlemi yan yana koyuyor: bir ham işaretçi kurmak, onu
taşmaya karşı tanımlı bir biçimde kaydırmak ve iki işaretçiyi birbiriyle karşılaştırmak.
Üçü de değeri **okumadığı** için, geçersiz bir adresle çalışıyor olsalar bile hiçbir şeyi
bozmazlar — bozacak olan, o adresteki veriye erişmeye kalkmaktır.

```rust
// izin.rs — hangi isaretci islemleri unsafe gerektirmiyor
fn main() {
    let v = vec![10, 20, 30, 40];
    let isaretci = v.as_ptr();                    // kurma
    let kaydirilmis = isaretci.wrapping_add(1);    // tasmaya karsi tanimli kaydirma
    let kendiyle_esit = isaretci == isaretci;
    let kaydirilmisla_esit = isaretci == kaydirilmis;

    println!("kendiyle esit mi: {}", kendiyle_esit);
    println!("kaydirilmisla esit mi: {}", kaydirilmisla_esit);
}
```

```
kendiyle esit mi: true
kaydirilmisla esit mi: false
```

Bu üç işlemin ortak yanı, sonuçlarının kendi başlarına hiçbir belleği açığa çıkarmamasıdır:
bir adresin kendisiyle eşit olup olmadığını bilmek, o adresteki değeri bilmekten farklıdır.
**DS1.** `wrapping_add` çağrısı burada bilinçli seçildi — dilin bu adı taşıyan işaretçi
yöntemi, kaydırma sonucu bir taşmaya uğrasa bile tanımlı bir değer döndürür ve bu yüzden
`unsafe` istemez; buna karşılık aynı kaydırmayı yapan `add` yöntemi, sonucun aynı ayrılmış
bellek bloğunun içinde ya da bir bayt ötesinde kalmasını çağırandan **ön koşul** olarak
ister ve bu ön koşul derleyici tarafından sınanmadığı için çağrısı `unsafe` gerektirir. İki
yöntem aynı aritmetiği yapar; aralarındaki fark, taşma durumunda neyin garanti edildiğidir.

Buna karşılık bir ham işaretçiyi **okumak** ya da **yazmak** ve gövdesi `unsafe` işaretli
bir işlevi çağırmak `unsafe` bloğu ister — çünkü bu işlemlerin ikisi de, derleyicinin
sınayamadığı bir varsayıma (adresin geçerli olduğuna, işlevin ön koşulunun sağlandığına)
dayanır. Aşağıdaki iki bölüm bu ayrımı aynı blok içinde koşturarak gösteriyor.

#### Aynı Blok İçinde Süren ve Duran Denetim

Bir `unsafe` bloğu, içine yazılan her satırı aynı ölçüde etkilemez. Aşağıdaki koşum aynı
blok içine iki farklı işlem koyuyor: biri bir ham işaretçiden doğrudan okuma, öteki bir
dilim üzerinde sınır denetimli bir sorgu. İkisi de aynı `unsafe` anahtar sözcüğünün altında
duruyor, ama yalnız biri denetimden muaf.

**DS2.** Dört elemanlı bir tam sayı dilimi kullanılıyor ve sorgulanan indis (onuncu öge)
bu dilimin sınırlarının dışında.

```rust
// blokA.rs — ayni unsafe blok icinde suren ve duran denetim
fn main() {
    let v = vec![10, 20, 30, 40];
    let isaretci = v.as_ptr();

    // Ayni blokta iki islem: biri ham isaretci uzerinden okuyor (denetimsiz),
    // digeri guvenli dilim sorgusu (v.get) yapiyor (hala denetimli).
    let (ilk_ham, onuncu) = unsafe { (*isaretci, v.get(10)) };

    println!("ham okuma: {}", ilk_ham);
    println!("onuncu oge: {:?}", onuncu);
}
```

```
ham okuma: 10
onuncu oge: None
```

İki satır da aynı `unsafe` bloğunun içinde çalıştı, ama farklı yollardan geçti. `*isaretci`
ham işaretçiyi doğrudan okudu — burada hiçbir sınır kontrolü yoktur, adres ne gösteriyorsa
o okunur. `v.get(10)` ise dilimin kendi yönteminden geçti; bu yöntemin gövdesi `unsafe`
işaretli değildir ve içindeki sınır denetimi hâlâ çalışıyor, bu yüzden dördüncü elemandan
sonrasını istediğinde çökmek yerine `None` döndü. **DS3.** `unsafe` bloğu burada yalnızca
`*isaretci` satırının önündeki engeli kaldırdı; `v.get` çağrısı zaten güvenli bir yöntemdi
ve `unsafe` bloğunun içine girmesi onun gövdesindeki denetimi değiştirmedi.

Bu gözlem dersin ana bulgusudur: `unsafe` bir anahtar değildir, derleyicinin bütün
kilitlerini birden açmaz. Blok yalnızca içine yazılan **belirli** işlemler için izin verir
— o işlemlerin dışındaki her şey, blok içinde bile, kendi kurallarına tabi kalır. Ödünç
denetleyicisi burada da devrede: `v`'nin sahipliği, `isaretci`nin ondan türetilmiş olması,
her ikisinin kapsam kuralları — hiçbiri `unsafe` bloğu yüzünden askıya alınmadı. Askıya
alınan tek şey, ham işaretçi okumasının kendi sınır denetimiydi.

Yukarıdaki koşum, güvenli çağrıyı `unsafe` bloğunun **içine** koyarak bunu bilerek gösterdi;
sıradan bir yazımda böyle bir zorunluluk yoktur, `v.get(10)` kendi başına bir `unsafe`
bloğu gerektirmez. Yaygın alışkanlık, bir `unsafe` bloğunu yalnız gerçekten gerektiren
satırlarla sınırlı tutmaktır — blok ne kadar genişlerse, hangi satırın hangi sözleşmeye
dayandığını sonradan ayırt etmek o kadar zorlaşır. Bu derste iki işlemin aynı blokta
yazılması, bu alışkanlığın neden var olduğunu somutlaştırmak için: blok geniş tutulduğunda
bile denetimin nerede sürdüğü, nerede durduğu kaynaktan okunabiliyor, ama bu okunabilirlik
otomatik değil, satır satır izlemeyi gerektiriyor.

#### Devredilen Üç İşlem

Önceki bölümdeki `*isaretci` okuması gibi, denetimin devredildiği satırlar tek başlarına
durmaz — her biri, aynı işi yapan güvenli bir karşılıkla yan yana konabilir. Aşağıdaki
koşum üç ayrı devredilmiş işlemi güvenli karşılıklarıyla karşılaştırıyor: ham işaretçi
aritmetiğiyle okuma, dilimin denetimsiz erişim yöntemi ve bir sözleşmeyi taşıyan `unsafe`
işlevin çağrısı.

Bu üç yazımın işlevi aynı olsa da kullanım yerleri farklıdır. Ham işaretçi aritmetiği,
bir bellek bloğunu Rust'ın kendi dilim tipinden bağımsız olarak dolaşmak gerektiğinde
kullanılır — örneğin bir yabancı işlevden dönen bir bellek bölgesini okurken, ki bu
konu bir sonraki derste ele alınıyor. `get_unchecked`, bir dilim üzerinde çalışırken
indisin geçerliliği zaten başka bir yerde (bir döngü sınırıyla, bir arama sonucuyla)
kanıtlanmışsa kullanılır. Sözleşmeli bir `unsafe` işlev ise bu iki yazımı bir arada
paketleyip, çağıran tarafa yalnızca imzayı okumasının yeterli olduğu bir arayüz sunmak
için kullanılır — sözleşme hâlâ vardır, ama gövde başka bir dosyaya taşınabilir.

**DS4.** Karşılaştırılan dilim yine `[10, 20, 30, 40]`; ham işaretçi ikinci indisten
başlayıp iki konum kaydırılıyor, denetimsiz erişim doğrudan ikinci indise gidiyor.

```rust
// blokB.rs — devredilen uc islem ve guvenli karsiliklari
unsafe fn ikinci_ogeyi_al(v: &[i32]) -> i32 {
    // Sozlesme: cagiran, v'nin en az iki oge tasidigini garanti eder.
    *v.get_unchecked(1)
}

fn main() {
    let v = vec![10, 20, 30, 40];
    let isaretci = v.as_ptr();

    let ham = unsafe { *isaretci.add(2) };
    let denetimsiz = unsafe { *v.get_unchecked(1) };
    let sozlesmeli = unsafe { ikinci_ogeyi_al(&v) };

    println!("ham isaretci okuma: {} (guvenli karsiligi {})", ham, v[2]);
    println!("denetimsiz erisim: {} (guvenli karsiligi {})", denetimsiz, v[1]);
    println!("sozlesmesi devredilen islev: {} (guvenli karsiligi {})", sozlesmeli, v[1]);
}
```

```
ham isaretci okuma: 30 (guvenli karsiligi 30)
denetimsiz erisim: 20 (guvenli karsiligi 20)
sozlesmesi devredilen islev: 20 (guvenli karsiligi 20)
```

Üç satırın da sonucu, `[]` ile yapılan sınır denetimli erişimle birebir aynı. Bu bir
rastlantı değil: `add(2)` ile ilerletilen işaretçi de, `get_unchecked(1)` çağrısı da,
`ikinci_ogeyi_al` işlevinin içindeki çağrı da, dilimin bellekteki düzenini doğru
okuyor — yalnızca bu doğruluğu kimin garanti ettiği değişiyor. Güvenli `[]` erişiminde
garantiyi dilimin kendi yöntemi verir ve indis sınırın dışındaysa çalışma anında panikle
durur. `get_unchecked` ve ham işaretçi aritmetiğinde bu garantiyi kimse vermez; doğru
indisi seçmek çağıranın yükümlülüğüdür.

`ikinci_ogeyi_al` işlevi bu yükümlülüğü kaynakta görünür kılan bir yazım göstermek için
var: imzasının önündeki `unsafe` sözcüğü, gövdenin çağrılmadan önce bir ön koşulun
sağlanmış olmasını beklediğini söyler. Bu, dilin kendisinin tanıdığı bir yazım kalıbıdır —
işlevin adı ve imzası, gövdesindeki denetimsiz erişimi çağıranın gözünden gizlemez, tersine
işaretler. Bir işlevi `unsafe` işaretlemek, gövdesinin içindeki her satırı otomatik olarak
denetimsiz kılmaz da: gövde içinde de yalnız gerçekten `unsafe` gerektiren satırlar
(burada `get_unchecked` çağrısı) o statüyü taşır, geri kalanı sıradan denetimli koddur. **DS5.** İşlevi `unsafe` çağıran taraf, `v`'nin en az iki eleman taşıdığını
kendisi doğrulamak zorunda; bu koşum sırasında dilim dört eleman taşıdığı için sözleşme
karşılandı ve sonuç güvenli karşılığıyla eşleşti.

Bu üç yöntemin dilde bulunmasının bir gerekçesi var: güvenli `[]` erişimi her çağrıda bir
sınır denetimi **adımı** yapar — indisin dilimin uzunluğundan küçük olup olmadığını
sınar, sonra okur. `get_unchecked` bu adımı atlar; okuduğu tek şey, çağıranın zaten
başka bir yerde doğruladığı bir indistir. Bu ders o adım farkını saymıyor, çünkü koşum
tek bir erişimle sınırlı ve süre bu müfredatta hiçbir yerde ölçülmüyor; önemli olan,
atlanan adımın **hangi denetim olduğunun** kaynaktan okunabilir olmasıdır — `get_unchecked`
adı, sınır denetiminin atlandığını doğrudan söyler, gizlemez.

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

Bu üç işlemin ortak özelliği, doğru kullanıldıklarında sonuçta hiçbir iz bırakmamalarıdır.
Yukarıdaki koşumda hem ham okuma hem denetimsiz erişim hem sözleşmeli çağrı, güvenli
karşılıklarıyla aynı sayıyı verdi — bir gözlemci yalnız çıktıya bakarak hangi satırın
`unsafe` olduğunu ayırt edemez. Fark sonuçta değil, **yanlış** kullanıldığında ne olacağının
kimin sorumluluğunda olduğundadır.

Yanlış kullanım bu derste koşturulmuyor, çünkü sınırın dışına taşan bir okuma tanımsız
davranıştır ve sonucu bu makineye, bu koşuma özgüdür — bir sonraki koşumda aynı hatayı
tekrar üretmeyebilir, sessizce yanlış bir değer okuyabilir ya da çöküşe yol açabilir.
Düzyazıyla tarif etmek yeterli: `isaretci.add(100)` çağrısı, dört elemanlık dilimin çok
ötesine geçen bir adresi okumaya çalışırdı. Derleyici bu çağrıyı reddetmez — çünkü reddetmek
için adresin ne gösterdiğini bilmesi gerekirdi ve bilmiyor. Aynı şekilde, `ikinci_ogeyi_al`
işlevi iki elemandan az bir dilimle çağrılsaydı, sözleşme ihlal edilmiş olurdu ve derleyici
bunu da fark etmezdi; imza yalnızca çağıranın bir sözü tuttuğunu varsayar, sözü sınamaz.

Bu, `unsafe` denetiminin ne yaptığını netleştiriyor: derleyici artık "bu doğru mu" diye
sormuyor, yalnızca "bu, kaynakta izin verilmiş bir yazım mı" diye soruyor. Sınırın nerede
durduğunu ölçmek, sınırın ötesinde ne olduğunu koşturmaktan ayrı bir iştir — bu ders
yalnız ilkini yapıyor.

Bu ayrım, `unsafe` işlevlerin neden bir yazım kuralına ihtiyaç duyduğunu da açıklıyor.
`ikinci_ogeyi_al` gibi bir işlevin üstüne, hangi ön koşulun beklendiğini anlatan bir
belge satırı eklemek dilin zorunlu tuttuğu bir şey değildir — ama yaygın bir yazım
alışkanlığıdır, çünkü imzadaki `unsafe` sözcüğü tek başına "hangi" ön koşulu söylemez,
yalnızca "bir" ön koşul olduğunu söyler. Bu koşumdaki işlev bu belgeyi bir satırlık bir
yorumla taşıyor; Belgelendirme dersi ileride bu tür bir belge satırının doğruluğunun da
kendi başına denetlenmediğini, ayrı bir sınıfa (**denetlenmiyor**) düştüğünü gösterecek.
Burada önemli olan, `unsafe`in kendisinin bu belgeyi zorunlu kılmaması, yalnızca dilin
kaynakta görünür bir işaret bırakmasıdır: `unsafe` sözcüğü olmadan bu işlev hiç
çağrılamaz, ama sözcüğün varlığı, çağıranın gerçekten doğru bir dilim geçirdiğini
göstermez.

#### Özet

- `unsafe` bloğu derleyicinin bütün denetimini kapatmaz; yalnız birkaç belirli işlemi
  (ham işaretçi okuma/yazma, `unsafe` işlev çağrısı gibi) kapsam dışına alır — aynı
  bloktaki güvenli bir çağrı (`v.get`) hâlâ **sürdürüyor** sınıfında kaldı
- Ham işaretçi aritmetiğiyle okuma, dilimin denetimsiz erişim yöntemi ve sözleşmeli bir
  `unsafe` işlevin çağrısı — üçü de denetimin yazana **devredildi**ği işlemlerdir
- Devredilen üç işlemin üçü de doğru kullanıldığında güvenli karşılığıyla birebir aynı
  sonucu verdi; fark sonuçta değil, yanlış kullanımın sorumluluğunda
- Ödünç denetleyicisi `unsafe` bloğu içinde de çalışmaya devam eder — kapanan bir
  denetleyici değil, yalnızca açılan birkaç işlem söz konusudur
- `unsafe` işaretli bir işlevin imzası, gövdesindeki ön koşulu çağırana görünür kılan bir
  yazım kalıbıdır; derleyici bu ön koşulu sınamaz, yalnız varlığını beyan ettirir

#### Sonraki Adım

Bu derste devredilen güvence hep aynı programın içinde kaldı — ham işaretçi de,
denetimsiz erişim de Rust'ın kendi bellek düzenini okuyordu; derleyici, işaretçinin
gösterdiği belleğin hangi tipten olduğunu ve dilimin gerçek uzunluğunu her zaman biliyordu,
yalnız bu bilgiyi okuma anında sınamayı bırakmıştı. Sıradaki ders aynı devri bir adım
öteye taşıyor: gövdesini derleyicinin hiç görmediği, başka bir dille yazılmış bir işlevi
çağırmak. Orada sözleşme yalnız bir ön koşul değil, bir **imza**dır — derleyici artık
karşı tarafın gövdesine hiç bakmadan, yalnız yazılan bildirime güvenerek derler.

### Yabancı Fonksiyon Arayüzü

Source: https://academia.sh/tr/kurslar/rust-ileri/yabanci-fonksiyon-arayuzu

Derleyicinin gövdesini hiç görmediği bir C işlevinin, yalnız bildirilen imzasına güvenilerek çağrılması; iki yabancı çağrının sonucunun Rust'ın kendi karşılığıyla eşleştiği koşturularak gösterilir ve bildirimin doğruluğunun yazanın sorumluluğunda olduğu açıklanır.

Önceki ders, güvencenin `unsafe` bir işlevin **imzasına** devredildiğini gösterdi:
`ikinci_ogeyi_al` işlevinin gövdesi Rust'la yazılmıştı, derleyici o gövdeyi okuyabilirdi,
yalnızca ön koşulu kendiliğinden sınamıyordu. Bu ders aynı devri bir adım öteye taşıyor:
gövdesi Rust'la yazılmamış, derleyicinin **hiç görmediği** bir işlevi çağırmak. Orada
sözleşme artık bir ön koşul cümlesi değil, yalnızca bir imzadır — parametre sayısı,
parametre tipleri, dönüş tipi.

Bu sınırın var olma nedeni pratiktir: bir programın ihtiyaç duyduğu her işlevi kendi
dilinde yeniden yazmak zorunlu değildir. İşletim sisteminin sağladığı çağrılar, uzun süredir
kullanılan sayısal ve metin işleme kitaplıkları, donanıma özgü sürücüler — bunların büyük
bölümü C ile yazılmıştır ve C'nin ikili uyumluluk kuralı, bir dile özgü olmayan ortak bir
sınır görevi görür. Rust bu sınırı **yeniden icat etmez**; C'nin çağrı kuralını doğrudan
konuşabilen bir yazım sunar ve karşı tarafın gövdesine dokunmadan onunla iletişim kurar.

#### Yalnızca İmzaya Güvenerek Çağırmak

Bir C kitaplığı işlevini Rust'tan çağırmak için o işlevin kaynağını görmeye, hatta aynı
makinede derlenmiş olmasına bile gerek yoktur — yaygın C işlevleri, işletim sisteminin
kendi standart kitaplığında **zaten bağlanmış** durumdadır. Rust programı yalnız bir
bildirim yazar: işlevin adı, aldığı parametrelerin tipleri, döndürdüğü değerin tipi. Bu
bildirim bir `extern "C"` bloğunun içine konur; `"C"` sözcüğü, çağrının hangi ikili
uyumluluk kuralına (parametrelerin yığıtta ya da yazmaçlarda hangi sırayla taşınacağına)
göre yapılacağını söyler — Rust'ın kendi işlevleri farklı bir kuralla derlenebilir, C
kitaplığıyla konuşmak için bu kuralın açıkça C'ninkine çevrilmesi gerekir.

Bildirim yazıldıktan sonra ek bir derleme adımı ya da bağlama adımı gerekmez: derleyici,
programı her zaman bağladığı sistem kitaplığına bu iki adı da arar ve bulur. Parametre
tipleri de elle uydurulmaz; `std::os::raw` modülü, `c_int` ve `c_char` gibi, C'nin `int`
ve `char` tiplerinin bu platformdaki genişliğine karşılık gelen adlar tanımlar — bunlar
Rust'ın kendi `i32` ya da `u8` tipleriyle aynı bellek düzenini paylaşır, ama adlandırma
bildirimin C tarafıyla eşleştiğini kaynaktan okunur kılar.

Sınır yalnız bu yönde işlemez. Bir Rust işlevi de, C tarafından çağrılabilecek bir imzayla
dışarı açılabilir; bu durumda derleyici o işlevin adını değiştirmeden bırakır ki bağlayıcı
onu C'nin beklediği adla bulabilsin. Bu ders yalnız Rust'tan C'ye giden yönü ölçüyor —
`abs` ve `strlen` her ikisi de sistemin sağladığı, Rust programının çağırdığı taraf. Ters
yön aynı sözleşme kuralına dayanır, yalnızca kaynakta hangi tarafın bildirim yazdığı
değişir.

Bildirimin kendisi ile bildirilen işlevi **çağırmak** aynı statüyü taşımaz. Bir
`extern "C"` bloğu yazmak, önceki derste ölçülen ham işaretçiyi kurmaya benziyor: derleyici
yalnızca bir adı ve bir imzayı kaydeder, henüz karşı tarafa geçmemiştir. Tehlike, o adın
gösterdiği yere gerçekten **atlandığında** başlar — bu yüzden yalnızca çağrı `unsafe` bloğu
ister, bildirimin kendisi istemez.

```rust
// bildirim.rs — bildirimin kendisi unsafe gerektirmiyor, cagirmak gerektiriyor
use std::os::raw::c_int;

extern "C" {
    fn abs(x: c_int) -> c_int;
}

fn main() {
    // Islevi bir gosterge olarak tasimak onu cagirmaz; unsafe gerekmiyor.
    let gosterge: unsafe extern "C" fn(c_int) -> c_int = abs;
    println!("bildirim derlendi, cagrilmadi: {}", gosterge as usize != 0);
}
```

```
bildirim derlendi, cagrilmadi: true
```

Bu koşum `abs`i hiç çağırmadı; yalnızca bildirilen işlevi bir değişkene taşıdı ve o
değişkenin boş olmadığını (sıfır adresi göstermediğini) doğruladı — gerçek adresin kendisi
hiçbir yerde basılmadı. **DS6.** Derleme bu satırda hiçbir uyarı vermedi çünkü bildirilen
işlev gerçekten kullanıldı, yalnızca çağrılmadı; kullanılmayan bir bildirim derleyici
tarafından ölü kod olarak işaretlenirdi. Bildirimin kendisinin `unsafe` istememesi, dilin
"bu isim bir yere işaret ediyor" demekle "o yere şimdi atlıyorum" demek arasında kurduğu
ayrımın bir başka örneğidir — önceki derste ham işaretçi kurmak ile onu okumak arasındaki
ayrımın aynısı.

#### İki Yabancı Çağrının Ölçümü

Aşağıdaki koşum iki yabancı işlevi çağırıyor: mutlak değer alan `abs` ve bir bayt dizisinin
uzunluğunu sayan `strlen`. İkisinin de gövdesi bu programın içinde yok — yalnız bildirimleri
var. Sonuçlar, aynı işi yapan Rust karşılıklarıyla karşılaştırılıyor.

**DS7.** Mutlak değeri alınan sayı `-7`; uzunluğu ölçülen dizge `"merhaba"` (yedi bayt,
hiçbir çok baytlı karakter taşımıyor).

```rust
// yabanci.rs — iki yabanci cagrinin sonucu ve rust karsiligi
use std::ffi::CString;
use std::os::raw::{c_char, c_int};

extern "C" {
    fn abs(x: c_int) -> c_int;
    fn strlen(s: *const c_char) -> usize;
}

fn main() {
    let mutlak = unsafe { abs(-7) };
    let metin = CString::new("merhaba").unwrap();
    let uzunluk = unsafe { strlen(metin.as_ptr()) };

    println!("yabanci mutlak: {} (rust karsiligi {})", mutlak, (-7i32).abs());
    println!("yabanci uzunluk: {} (rust karsiligi {})", uzunluk, "merhaba".len());
}
```

```
yabanci mutlak: 7 (rust karsiligi 7)
yabanci uzunluk: 7 (rust karsiligi 7)
```

İki çağrı da `unsafe` bloğunun içinde — ama önceki dersteki `unsafe`den farklı bir
gerekçeyle. Orada tehlike, dilimin sınırlarının dışına taşan bir okumaydı; burada tehlike,
derleyicinin karşı tarafın **hiçbir şeyini** bilmemesidir. `abs` ve `strlen` işlevlerinin
kaynak kodu bu programın hiçbir yerinde yok; derleyici onları görmeden, yalnız yukarıdaki
iki satırlık bildirime güvenerek çağrıyı üretti. **DS8.** Sonuç iki satırda da birebir
eşleşti: yabancı `abs` çağrısı `7` döndürdü, Rust'ın kendi `abs` yöntemi de `7`; yabancı
`strlen` çağrısı `7` döndürdü, Rust'ın `len` yöntemi de `7`. Eşleşme tesadüf değil —
bildirimin C tarafındaki gerçek imzayla birebir örtüşmesinin sonucu.

`CString::new` çağrısı burada bilinçli bir adım: Rust'ın kendi dizgi tipi, sonunda sıfır
baytı bulundurmaz ve uzunluğunu ayrı bir sayaçta tutar; C'nin dizgi kuralı ise sonu sıfır
baytıyla biten bir bayt dizisidir. `strlen`i doğrudan bir Rust dizgesiyle çağırmak, iki
kuralın farklı olduğu bir sınırda tip uyuşmazlığına yol açar; `CString`, aradaki çeviriyi
yaparak sonunda sıfır baytı olan bir bayt dizisi kurar ve yalnız o dizinin başlangıç
adresini `strlen`e geçirir.

#### Sınırın İki Tarafındaki Ayrı Kurallar

Bu derste sınırı geçen değerler yalnızca tek tam sayılar ve bayt dizileri; bir yapı
(struct) sınırı geçseydi ayrı bir kural daha devreye girerdi. Rust'ın kendi yapı
düzenleyicisi, alanları derleme sırasında istediği gibi yeniden sıralayabilir — bu,
programın davranışını değiştirmeyen, yalnız bellekteki yerleşimi etkileyen bir
serbestliktir. C tarafı ise bir yapının alanlarını bildirildiği sırayla, hizalama
kurallarına göre art arda yerleştirmeyi **garanti eder**. İki taraf da aynı adları taşıyan
bir yapı üzerinde anlaşsa bile, biri alanları yeniden sıralarken öteki sıralamayı sabit
tutuyorsa, aynı bellek bloğu iki farklı yorumla okunur. Bu ders yapı geçirmiyor, yalnızca
tam sayı ve işaretçi geçiriyor; sınırın bu parçası burada ölçülmüyor, yalnızca var
olduğu kaynaktan anılıyor.

`abs` ve `strlen` seçilme nedeni, ikisinin de belleği yalnızca **okumasıdır** — hiçbiri
yeni bir bellek bloğu ayırıp Rust tarafına bırakmıyor. Bu, FFI sınırındaki en önemli
kısıtlardan birini gösteriyor: bir C işlevi kendi içinde bellek ayırıp bir işaretçi
döndürseydi, o belleği kimin, hangi ayırıcıyla serbest bırakacağı ayrı bir sözleşme
konusu olurdu. Rust'ın kendi büyüyen dizisi ve C'nin `malloc` işlevi aynı ayırıcıyı
kullanmak zorunda değildir; C tarafında ayrılan bir bellek bloğunu Rust'ın kendi bırakma
kuralıyla serbest bırakmaya kalkmak, iki ayrı ayırıcının defterini karıştırır ve bu da
tanımsız davranış üretir. Bu ders bu durumu koşturmuyor — `abs` bir tam sayı, `strlen` bir
uzunluk döndürüyor, ikisi de yeni bellek ayırmıyor — ama sınırın bu kısmının varlığını
kaynaktan görünür kılmak için anılıyor.

Sınırın her parçası `devredildi` ya da `dilin dışında` sınıfına düşmüyor; `CString::new`
çağrısı bunlardan birine ait değil. C'nin sıfır baytıyla biten dizgi kuralı, bir dizginin
içinde sıfır bayt **taşıyamayacağı** anlamına gelir — sıfır bayt görüldüğü an dizgi orada
biter. `CString::new`, verdiği bayt dizisinin içinde bir sıfır bayt olup olmadığını
çalışma zamanında sınar ve varsa çağrıyı `panik` ile değil bir hata değeriyle durdurur.

```rust
// sifir_bayt.rs — CString::new govde icindeki sifir bayti denetliyor
use std::ffi::CString;

fn main() {
    let temiz = CString::new("merhaba");
    let bozuk = CString::new("me\0rhaba");

    println!("temiz dizge kuruldu mu: {}", temiz.is_ok());
    println!("govde ici sifir bayt reddedildi mi: {}", bozuk.is_err());
}
```

```
temiz dizge kuruldu mu: true
govde ici sifir bayt reddedildi mi: true
```

**DS9.** Bu, sınırın `strlen` çağrısındaki devredilmiş kısmından farklı bir yer: burada
denetim derleyicide değil çalışma zamanında yapılıyor, ama yine de **yapılıyor** — `Result`
tipi çağırana hatayı görmezden gelme izni vermiyor. Sınırın tamamı yazana devredilmiş
değildir; C'nin kendi biçim kuralına uymayan bir girdi, daha yabancı çağrıya hiç
ulaşmadan durduruluyor.

Hata bildirme biçimi de sınırın iki tarafında ayrıdır. Rust'ta bir işlemin başarısızlığı
genellikle bir `Result` değeriyle taşınır ve derleyici bu değerin ele alınıp alınmadığını
denetler. C'nin böyle bir tipi yoktur; bir işlevin başarısız olduğu genellikle özel bir
dönüş değeriyle (örneğin negatif bir sayı ya da sıfır) ya da ayrı bir global durumla
işaretlenir. `extern "C"` bildirimi bu farkı ortadan kaldırmaz — bildirilen dönüş tipi
`c_int` ise, o değerin bir hata kodu mu yoksa sıradan bir sonuç mu olduğunu bilmek yine
çağıranın C tarafının belgesini okumasına bağlıdır; derleyici bu ayrımı tipten çıkaramaz,
çünkü tip ikisi için de aynıdır.

Karakter kodlaması, `strlen`in döndürdüğü sayının ne anlama geldiği konusunda ayrı bir
tuzak taşıyor. Rust'ın kendi `len` yöntemi de, `strlen` de aynı şeyi sayar: dizginin
**bayt** uzunluğunu — dizginin görsel olarak kaç karakterden oluştuğunu değil. Bu derste
kullanılan `"merhaba"` yalnızca tek baytlık karakterlerden oluştuğu için bu ayrım
görünmedi; birden çok baytla kodlanan bir karakter taşıyan bir dizgede iki sayı birbirinden
ayrılır.

```rust
// bayt_karakter.rs — bayt uzunlugu ile karakter sayisinin ayrilmasi
use std::ffi::CString;
use std::os::raw::c_char;

extern "C" {
    fn strlen(s: *const c_char) -> usize;
}

fn main() {
    let dizge = "şafak";
    let dizi = CString::new(dizge).unwrap();
    let yabanci_uzunluk = unsafe { strlen(dizi.as_ptr()) };

    println!("rust bayt uzunlugu: {}", dizge.len());
    println!("rust karakter sayisi: {}", dizge.chars().count());
    println!("yabanci strlen: {}", yabanci_uzunluk);
}
```

```
rust bayt uzunlugu: 6
rust karakter sayisi: 5
yabanci strlen: 6
```

**DS10.** `dizge.len()` ile yabancı `strlen` çağrısı burada da birebir eşleşti (`6`) —
ikisi de bayt sayıyor. Ayrı düşen, `chars().count()` yöntemiyle bulunan karakter sayısı
(`5`): dizgedeki `ş` harfi Rust kaynağında tek bir karakter olarak yazılsa da UTF-8
kodlamasında iki bayt tutuyor. `strlen`e güvenen bir C tarafı bu sayıyı her zaman bayt
olarak okur; onu karakter sayısı sanan bir çağıran, tek baytlık olmayan bir dizgede yanlış
bir varsayımla ilerlemiş olur. Derleyici bu yanlış varsayımı da fark edemez — dönüş tipi
`usize`dir ve `usize`in bir bayt sayısı mı yoksa karakter sayısı mı taşıdığını söylemez.

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

Bu iki çağrının doğru çalışması, bildirimin doğru yazılmış olmasına bağlıydı — ve bu
doğruluğu derleyici hiçbir noktada sınamadı. `abs`in gerçek C imzası tek bir tam sayı alıp
tek bir tam sayı döndürür; bildirim bunu doğru yazdığı için çağrı doğru çalıştı. Bildirim
yanlış yazılsaydı — örneğin `abs`in iki parametre aldığı ya da bir kayan noktalı sayı
döndürdüğü bildirilseydi — derleyici bunu **fark edemezdi**. Derleyicinin elinde karşı
tarafın gövdesi yok; yalnızca yazılan bildirime bakıyor ve o bildirime uygun bir çağrı
üretiyor. Yanlış bir bildirimin sonucu bu derste koşturulmuyor, çünkü sonucu bu makineye
özgü ve tanımsız davranış sınıfına girer — yığıttaki değerler yanlış tipte yorumlanabilir,
program yanlış bir sayı üretebilir ya da çökebilir.

Bu, `devredildi` ile `dilin dışında` sınıflarının kesiştiği yerdir. Denetim, önceki
derste olduğu gibi yazana devredildi — ama burada devredilen şey bir ön koşul cümlesi
değil, karşı tarafın **doğruluğuna** dair hiçbir garantisi olmayan bir imzadır. Karşı
tarafın kendisi Rust'ın dışında yazılmış, Rust'ın derleme sürecinin dışında derlenmiş ve
sistem tarafından zaten bağlanmıştır; derleyicinin ona dair tek bilgisi, programcının
yazdığı iki satırdır. Bildirim doğruysa iki dil arasındaki sınır görünmez olur — yukarıdaki
koşum tam olarak bunu gösterdi. Bildirim yanlışsa, hatanın nerede olduğunu derleyici değil,
yalnızca çalışma zamanındaki gözlem ortaya çıkarabilir.

Bu sınırın genişliği, tek bir yanlış bildirimle değil, bildirimin **her alanıyla** ölçülür.
Parametre sayısı yanlışsa, çağrı yığıttan ya da yazmaçlardan okuduğu yerleri kaydırır ve
sonraki her parametre de yanlış bir değer taşır. Parametre tipi doğru genişlikte değilse —
örneğin gerçek C imzası dört baytlık bir tam sayı beklerken bildirim sekiz baytlık bir tam
sayı yazıyorsa — çağıran taraf, karşı tarafın okumadığı fazladan baytları da göndermiş ya
da eksik göndermiş olur. Dönüş tipi yanlışsa, çağrının döndürdüğü baytlar yanlış bir tipe
göre yorumlanır. Üç durumun da ortak noktası, derleyicinin hiçbirini reddetmemesidir:
`extern "C"` bloğu sözdizimsel olarak geçerli bir bildirimdir, C tarafındaki gerçek
imzayla örtüşüp örtüşmediği derlemenin konusu değildir.

#### Özet

- `extern "C"` bloğu, gövdesi Rust'ın dışında yazılmış bir işlevi yalnızca imzasıyla
  bildirir; derleyici o gövdeyi hiçbir zaman görmez, güvencesi tamamen **dilin dışında**
  kalır
- C kitaplığının yaygın işlevleri sistemde zaten bağlıdır; bildirim yazıldıktan sonra ek
  bir derleme ya da bağlama adımı gerekmez
- İki yabancı çağrının (`abs`, `strlen`) sonucu, Rust'ın kendi karşılığıyla birebir
  eşleşti — eşleşme, bildirimin gerçek C imzasıyla doğru yazılmış olmasının sonucudur
- `CString`, Rust'ın uzunluk sayaçlı dizgesi ile C'nin sıfır baytıyla biten dizisi
  arasındaki kural farkını, sınırda açık bir dönüşümle kapatır
- Bildirim yanlış yazılsaydı derleyici bunu fark edemezdi; doğruluk tamamen yazanın
  sorumluluğundadır — bu, `devredildi` ile `dilin dışında` sınıflarının kesiştiği nokta
- Sınırın her parçası devredilmiş değildir: `CString::new`, gövde içinde bir sıfır bayt
  olup olmadığını çalışma zamanında sınar ve varsa bir hata değeriyle durdurur — bu
  denetim çalışma zamanında olsa da hâlâ **sürdürüyor**

#### Sonraki Adım

Bu derste sınır, iki dil arasındaydı; bildirim doğruysa hangi makinede koştuğunun önemi
yoktu — `abs` ve `strlen` çağrıları, program hangi işlemcide ya da hangi işletim
sisteminde koşarsa koşsun aynı sonucu üretti, çünkü ikisi de sistemin kendi standart
kitaplığına bağlanıyordu. Sıradaki ders bu bağımsızlığı tersine çeviriyor: aynı kaynağın,
koştuğu **hedefe** göre farklı bir gövdeye ihtiyaç duyması. Orada güvence bir bildirime
değil, hedefin kendi verdiği kısıtlara bağlı olacak — ve o kısıt, bu derste olduğu gibi
bir dış kitaplığın imzasında değil, kaynağın kendisinde koşullu olarak yazılacak.

### WebAssembly Hedefi

Source: https://academia.sh/tr/kurslar/rust-ileri/webassembly-hedefi

Aynı kaynağın koştuğu hedefe göre farklı bir gövdeye ihtiyaç duyması: standart kütüphanenin bir parçasına (dosya sistemi) dayanan bir gövde ile hiç dayanmayan bir gövde koşullu derlemeyle ayrılır, ikisi de yerel hedefte koşturulur ve aynı sonucu verir.

Önceki ders, sınırın iki **dil** arasında olduğu durumu ölçtü: bildirim doğruysa, hangi
işlemcide ya da işletim sisteminde koştuğunun önemi yoktu. Bu ders aynı bağımsızlığı
tersine çeviriyor. Burada dil aynı kalıyor — kaynağın tamamı Rust — ama koştuğu **hedef**
değişiyor ve bazı gövdeler bu değişikliğe kayıtsız kalamıyor. Bir tarayıcıda koşan bir
hedef, bir dosya sistemine, bir işletim sistemi iş parçacığına ya da bir saate her zaman
erişemez; bu ders o erişimin kaynakta nasıl **görünür** kılındığını ölçüyor.

Önceki dersteki sınır bir bildirimin doğruluğuna, buradaki sınır ise hedefin sunduğu
ortamın kendisine bağlı; ikisi de derleyicinin doğrudan sınayamadığı bir varsayıma
dayanıyor. Bu kısıt Rust'a özgü değil, tarayıcının kendi güvenlik modelinden geliyor. Bir tarayıcı
sekmesi, çalıştırdığı kodu diskten, ağdan ve işletim sisteminden yalıtılmış bir alanda
tutar; sayfanın kendi başına bir dosya açması, bir iş parçacığı oluşturması ya da bir
soket kurması, yalıtımın amacına aykırı düşer. Bu alana derlenen bir program, o alanın
sunduğu olanaklarla sınırlıdır — ve bu olanaklar, aynı programın bir sunucuda ya da bir
masaüstünde koşan sürümünün sahip olduklarından daha dardır. Rust'ın standart kütüphanesi
bu farkı gizlemez: dosya sistemi, iş parçacığı ve zaman gibi modüllerin bir kısmı, bu
yalıtılmış hedefte ya hiç yoktur ya da farklı davranır.

Bu derste yalnız yerel hedef derlenir. Başka bir hedef için gerçek bir derleme
yapılmıyor; hedefe göre değişen karar, koşullu derleme ile **yerel hedefte** gösteriliyor
— kaynaktaki iki gövdeden hangisinin bu makinede seçildiği koşturularak görülüyor, öteki
gövde ise hiç derlenmeden kaynakta duruyor. Bu seçim çalışma zamanında yapılmaz: `cfg`
tarafından yönetilen karar, program çalışmaya başlamadan önce, derleme sırasında verilir.
Aynı ikili dosyanın içinde iki gövdeden birini seçip çalışma anında diğerine geçmek
mümkün değildir — hangi hedef için derlendiyse, o hedefin gövdesi ikili dosyaya girer,
öteki hiç girmez.

#### Hedefe Göre Ayrılan Gövde

`#[cfg(...)]` bir çalışma zamanı dallanması değildir; bir `if` ifadesiyle karıştırılmamalı.
`if` çalışma anında bir koşulu sınar ve iki dalın da ikili dosyada yer aldığı, yalnız
birinin çalıştırıldığı bir yapıdır. `cfg` ise derleme anında karar verir: koşulu
sağlamayan öğe, kaynak ağacından hiç kesilmiş gibi, derlemenin geri kalanına hiç
girmez. Bu farkın sonucu, aşağıdaki koşumda görülüyor.

Aşağıdaki koşum bir sensör kaydını okuyan iki gövde tanımlıyor. Biri dosya sisteminden
okuyor; öteki, tarayıcı hedefinde derlenecek bir gövdenin yerini tutuyor ve bu makinede
hiç derlenmiyor. Hangi gövdenin derleneceğine `#[cfg(...)]` özniteliği karar veriyor.

**DS11.** Sensör kaydı dört satırlık bir metin: `12`, `7`, `5`, `21` — toplamı `45`.

```rust
// wasm1.rs — hedefe gore ayrilan govde, yerel hedefte secilen dal
use std::fs;

#[cfg(not(target_family = "wasm"))]
fn kayitlari_oku() -> String {
    // Dosya sistemine dayanan govde: yalnizca dosya sistemi olan hedeflerde derlenir.
    fs::read_to_string("sensor.txt").expect("dosya okunamadi")
}

#[cfg(target_family = "wasm")]
fn kayitlari_oku() -> String {
    // Bu govde tarayici hedefinde derlenir; yerel hedefte hic derlenmez,
    // bu yuzden burada tanimlanmamis bir cagri tasimasi hicbir sey bozmaz.
    govde_wasm_hedefinde_baska_bir_kaynaktan_okur()
}

fn toplam(metin: &str) -> i32 {
    metin.lines().filter_map(|s| s.trim().parse::<i32>().ok()).sum()
}

fn main() {
    fs::write("sensor.txt", "12\n7\n5\n21\n").expect("dosya yazilamadi");
    let icerik = kayitlari_oku();
    println!("dosya sistemine dayanan govde toplami: {}", toplam(&icerik));
}
```

```
dosya sistemine dayanan govde toplami: 45
```

Kaynakta iki `kayitlari_oku` tanımı var, ama derlenen program yalnız birini içeriyor.
`#[cfg(not(target_family = "wasm"))]` işaretli gövde, bu makine wasm ailesinden
olmadığı için derlemeye girdi; `#[cfg(target_family = "wasm")]` işaretli gövde ise hiç
derlenmedi — içindeki çağrı hiçbir yerde tanımlı olmadığı hâlde derleyici bunu fark
etmedi, çünkü o gövdeyi hiç görmedi. **DS12.** Bu, cfg ile dışarıda bırakılan bir gövdenin
söz dizimi geçerli olduğu sürece hiçbir semantik denetimden geçmediğini gösteriyor;
"derlenmeyen" ile "yanlış" aynı şey değil, derlenmeyen gövde hakkında derleyici hiçbir
yargı vermiyor.

Bu, önceki dersteki yabancı işlev bildirimine benziyor görünse de ayrı bir güvence
taşıyor. `extern "C"` bildirimi, derleyicinin hiç görmediği ama gerçekten var olan ve
bağlanması gereken bir gövdeyi tarif ediyordu; bağlayıcı o adı bulamazsa derleme çöküyordu.
`#[cfg(target_family = "wasm")]` ile işaretli gövde ise bu makinede **hiç aranmıyor** —
içinde geçen `govde_wasm_hedefinde_baska_bir_kaynaktan_okur` adının bu programın hiçbir
yerinde tanımlı olmaması önemli değil, çünkü o ad hiçbir zaman bağlayıcıya ulaşmıyor.
Yabancı fonksiyon arayüzünde devredilen güvence bir bildirimin doğruluğuydu; burada
devredilen hiçbir şey yok, çünkü o gövde bu derlemenin bir parçası bile değil.

`kayitlari_oku` içindeki `.expect(...)` çağrısı da bilinçli bir seçim: dosya, aynı
`main` işlevi içinde bir satır önce yazıldığı için okuma başarısız olmayacağı biliniyor,
ama Rust bunu derleme zamanında bilemiyor — `read_to_string` her zaman bir `Result`
döndürür ve bu değer ele alınmak zorunda. `.expect(...)` bu değeri açıp, başarısızlık
durumunda panikle durmayı seçiyor; bu ders panik tetiklemediği için bu yol yalnızca
başarı durumunda izleniyor.

Yukarıdaki koşum `target_family = "wasm"` yazımını seçiyor; dilin tanıdığı öznitelik
adlarından biri de `target_arch = "wasm32"`dir ve ikisi aynı şeyi sormaz. `target_family`,
bir hedefin ait olduğu geniş aileyi (wasm ailesi, unix ailesi gibi) sorar; `target_arch`
ise işlemci mimarisinin adını (`wasm32`, `x86_64` gibi) sorar. Bu derste aile sorulmuştu,
çünkü ölçülen ayrım mimariye değil, hedefin sunduğu ortam sınıfına dayanıyordu — dosya
sistemi bulunmayan bir hedef, tek bir mimariyle sınırlı değildir. Hangi öznitelik adının
seçildiği kaynaktan okunabilir bir karardır ve gövdenin hangi soruya göre ayrıldığını
söyler.

#### Dosya Sistemine Dayanmayan Karşılık

Bir gövdenin `cfg` ile ayrılması, o gövdenin varlığını sürdürmesi gerektiği anlamına
gelmez. Aynı sensör kaydı, dosya sistemine hiç dokunmadan da elde edilebilir — bu ders
için değer kaynağın içine gömülü. Bu iki seçenek arasındaki fark bir uygulama ayrıntısı
değildir: ilki verinin **çalışma zamanında** bir kaynaktan geldiğini varsayar, ikincisi
verinin **derleme zamanında** zaten bilindiğini varsayar. Hangi varsayımın geçerli
olduğu, çözülen problemin kendisine bağlıdır — sensör kaydı gerçekten değişen bir veriyse
dosya (ya da onun yerini tutan başka bir kaynak) gerekir; sabit bir yapılandırma değeriyse
gömülü sabit yeterlidir.

```rust
// wasm2.rs — dosya sistemine hic dayanmayan, hedeften bagimsiz govde
const GOMULU_KAYITLAR: &str = "12\n7\n5\n21\n";

fn toplam(metin: &str) -> i32 {
    metin.lines().filter_map(|s| s.trim().parse::<i32>().ok()).sum()
}

fn main() {
    let toplam_deger = toplam(GOMULU_KAYITLAR);
    println!("gomulu govde toplami: {}", toplam_deger);
}
```

```
gomulu govde toplami: 45
```

İki gövde de aynı sonucu (`45`) verdi, ama farklı yollardan. Dosya sistemine dayanan
gövdenin çalışması için ortamın bir dosya sistemi **sunması** gerekiyordu — bu makinede
sunuluyor, ama bir tarayıcı hedefinde bu varsayım genellikle geçerli değildir, çünkü
tarayıcı sayfası doğrudan bir dosya sistemine erişemez. Gömülü gövde ise hiçbir dış
kaynağa bağlı değil; verisi kaynağın kendisinde, derleme anında sabitleniyor. **DS13.**
Aynı sonucu üreten bu iki yazım, hedef bağımsız bir görevi (dört sayıyı toplamak) hedefe
bağlı bir kaynaktan (dosya) ve hedeften bağımsız bir kaynaktan (gömülü sabit) besleyerek
gösteriyor.

Verinin kaynağa gömülmesi burada `const` bildirimiyle yapıldı; değer derleme sırasında
ikili dosyanın bir parçası olur, çalışma zamanında hiçbir dış kaynak istemez. Bu yazım
yalnızca tarayıcı hedefiyle sınırlı bir çözüm değildir — bir dosya sisteminin **hiç
bulunmadığı**, yalnızca sabit bir bellek düzeninin var olduğu ortamlar için de aynı
yaklaşım kullanılır. Sıradaki ders bu ortamı doğrudan ele alacak; oradaki kısıt tarayıcının
yalıtılmış alanının değil, standart kütüphanenin kendisinin yokluğudur.

#### İkinci Bir Ayrım: İş Parçacığı

Dosya sistemi tek örnek değil. Aynı kalıp, birden çok iş parçacığı açma yeteneği için de
geçerli — bir tarayıcı sayfasının çalıştırdığı kod, işletim sisteminin sıradan iş
parçacığı oluşturma çağrısına her zaman erişemez. Bir iş parçacığı açmak, işletim
sisteminden yeni bir yürütme birimi ayırmasını ve bu birimi zamanlamasını ister; bu,
dosya açmaktan farklı bir hizmet ama aynı sınıftan bir istek — ikisi de tek başına
kaynağın içinde yürütülemez, ortamın bir şey **sağlamasını** bekler. Aşağıdaki koşum aynı
`cfg` düzenini bir kez daha kuruyor: yerel hedefte derlenen gövde bir iş parçacığı açıp
sonucunu topluyor, wasm ailesi için yazılmış gövde ise yine bu makinede hiç derlenmiyor.

**DS14.** Hesaplanan değer `6 * 7`, yani `42`; iş parçacığının döndürdüğü değer ana
gövdeye `join` çağrısıyla toplanıyor.

```rust
// wasm3.rs — is parcasina dayanan govde, yerel hedefte secilen dal
use std::thread;

#[cfg(not(target_family = "wasm"))]
fn deger_uret() -> i32 {
    // Is parcasi acan govde: coklu is parcasi destegi olmayan hedeflerde derlenmez.
    let is_parcasi = thread::spawn(|| 6 * 7);
    is_parcasi.join().expect("is parcasi cokmus olamaz")
}

#[cfg(target_family = "wasm")]
fn deger_uret() -> i32 {
    wasm_hedefinde_ayri_bir_yontemle_hesaplanir()
}

fn main() {
    println!("is parcasina dayanan govde: {}", deger_uret());
}
```

```
is parcasina dayanan govde: 42
```

Bu koşumda da fark aynı yerde: `thread::spawn` çağrısı ayrı bir iş parçacığı açılmasını
işletim sisteminden ister ve bu istek her hedefte karşılanamaz. `#[cfg(not(target_family
= "wasm"))]` etiketi bu bağımlılığı üstlenen gövdeyi işaretliyor; aynı hesabı doğrudan,
hiçbir iş parçacığı açmadan yapan bir gövde de yazılabilirdi (`6 * 7` tek satırda
hesaplanır) ve o gövde her hedefte aynı şekilde çalışırdı. Buradaki nokta önceki
ölçümle aynı: hedefe bağlı olan, hesabın kendisi değil, hesabı **nasıl** yürüttüğüdür.

Bu iki örnek (dosya, iş parçacığı) arasında bir de fark var: dosya sisteminin yokluğu
genellikle bir hata değeriyle kendini gösterir — çağrı bir `Result` döndürür ve başarısız
olursa bu değer üzerinden anlaşılır. İş parçacığı açma yeteneğinin durumu hedefe göre
değişir; bazı hedeflerde işlev hiç bulunmaz ve kaynak o işlevi kullanan haliyle
derlenmez, bazı hedeflerde ise farklı bir kısıtla (örneğin tek bir iş parçacığıyla
sınırlı olarak) çalışabilir. İki durumda da ortak olan, bu farkın kaynakta bir yerde
görünür olması gerektiğidir — ya bir `cfg` etiketiyle, ya da dönen değerin ele alınma
biçimiyle.

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

Yukarıdaki iki koşum da bu makinede aynı sonucu verdi — ama bu, dosya sistemine dayanan
gövdenin her hedefte çalışacağı anlamına gelmez. Kaynağın kendisi, hangi kısmının hedefe
bağlı olduğunu zaten söylüyor: `#[cfg(not(target_family = "wasm"))]` etiketi, altındaki
gövdenin wasm ailesi dışındaki hedeflerde derleneceğini, `#[cfg(target_family = "wasm")]`
etiketi ise karşıt gövdenin yalnız wasm hedefinde derleneceğini beyan ediyor. Bu bilgiye
ulaşmak için o hedefte bir koşum çalıştırmaya gerek yok; etiketin kendisi, koşuma
bakmadan okunabilen bir bildirim.

Bunun tersi de doğru: `cfg` etiketi taşımayan bir gövde, otomatik olarak her hedefte
çalışacağı anlamına gelmez. `toplam` işlevi bu derste hiçbir `cfg` taşımıyor ve iki
gövde tarafından da paylaşılıyor — çünkü yalnız metin ayrıştırma ve toplama yapıyor, dış
bir kaynağa dokunmuyor. Ama bir işlev `cfg` taşımadığı hâlde içinde iş parçacığı açan,
gerçek zamanı okuyan ya da bir soket bağlantısı kuran bir çağrı taşısaydı, bu bağımlılık
kaynaktan **görünmezdi** — derleyici böyle bir çağrıyı reddetmez, yalnızca o hedefte
derlerken ya derleme hatası verir ya da çalışma zamanında beklenmeyen bir sonuç üretir.
Kaynağın taşınabilir görünmesi, taşınabilir olduğunu göstermez; hangi varsayımın hedefe
bağlı olduğu ancak kaynaktaki `cfg` etiketlerinden ve kullanılan standart kütüphane
parçalarından okunabilir, bir koşumdan değil.

Bu derste ölçülen iki örnek (dosya sistemi, iş parçacığı) tükenmez bir liste değil, aynı
sınırın iki yüzü. Gerçek zamanın okunması da benzer bir sınıra girer: bir hedefin sistem
saatine erişimi olmayabilir ya da bu erişim farklı bir yolla sağlanabilir. Üç örneğin
ortak yanı, hepsinin işletim sisteminden ya da donanımdan bir **hizmet istemesidir** —
dosya sistemi bir depolama hizmeti, iş parçacığı bir zamanlama hizmeti, saat bir donanım
sayacı hizmeti ister. Yalıtılmış bir hedef bu hizmetlerin bir kısmını hiç sunmaz ya da
farklı bir arayüzden sunar; kaynak, hangi işlevin hangi hizmeti istediğini `cfg`
etiketleriyle ya da kullanılan modülün kendisiyle işaretlediği ölçüde okunabilir kalır.
Üçü de bu derste koşturulmuyor; yalnızca dosya sistemi ve iş parçacığı örnekleri
koşturuldu, saat örneği aynı sınıfa ait olduğu için yalnızca anılıyor.

Bu okunabilirlik, gerçek çok hedefli kaynakların (birden çok işletim sistemi ve mimaride
derlenmesi beklenen kütüphanelerin) neden `cfg` etiketleriyle dolu göründüğünün de
nedenidir; bu ders boyunca kurulan iki örnek, o kaynaklardaki düzenlerin küçük ölçekli
bir modelinden ibaret. Bir kaynağı bir hedefe taşımadan önce güvenilir bir yöntem, kaynaktaki `cfg`
etiketlerini ve dış hizmet isteyen çağrıları taramaktır — bu derste yapılan tam olarak
budur, yalnızca tek bir hedef çiftinde (yerel hedef ile wasm ailesi) ve tek bir gövde
üzerinde.

#### Özet

- Hedefe göre değişen bir karar, `#[cfg(...)]` öznitelikleriyle kaynakta iki ayrı gövdeye
  bölünebilir; yerel hedefte yalnız biri derlenir, öteki hiç derlenmeden kaynakta durur —
  `cfg` bir `if` gibi çalışma anında dallanmaz, derleme anında karar verir
- Tarayıcı hedefi için gerçek bir derleme yapılmadı; hedefe bağlı karar kaynak
  üzerinden **modellendi**
- Dosya sistemine dayanan gövde ile dayanmayan gövde aynı sensör toplamını (`45`) verdi;
  fark sonuçta değil, hangi ortam varsayımına dayandıklarında
- Bir tarayıcı hedefinin dosya sistemi gibi bazı ortam olanaklarını sunmaması, bu hedefin
  verdiği güvencenin dilin kendisinden değil hedeften geldiğini gösterir — sınıf **dilin
  dışında**
- Dosya sistemi ile iş parçacığı açma aynı sınırın iki ayrı örneği: ikisi de işletim
  sisteminden bir hizmet ister ve bu hizmet her hedefte aynı biçimde sunulmaz
- Kaynağın taşınabilir görünmesi taşınabilir olduğu anlamına gelmez; hedef bağımlılığı
  `cfg` etiketlerinden ve kullanılan standart kütüphane parçalarından, koşumdan değil,
  kaynaktan okunur

#### Sonraki Adım

Bu derste sınır, aynı dilin iki hedefi arasındaydı; fark, ortamın hangi olanakları
sunduğuydu — dosya sistemi de iş parçacığı da işletim sisteminin **var olduğu**
varsayımına dayanıyordu, yalnızca o varsayımın karşılanıp karşılanmadığı hedeften
hedefe değişiyordu. Sıradaki ders bu sınırı bir uca taşıyor: standart kütüphanenin **hiç**
bulunmadığı, işletim sisteminin kendisinin olmadığı bir ortam. Orada gömülü sabit gibi
hedeften bağımsız bir yazım bile, artık bellek ayırmanın kendisinin var olup olmadığı
sorusuyla karşılaşacak — bu derste "hedefe bağlı" olan yalnız birkaç işlevdi, orada
"hedefe bağlı" olan, programın bütün gövdesinin nasıl yazılacağını belirleyen bir
kısıttır.

### Gömülü Hedefler

Source: https://academia.sh/tr/kurslar/rust-ileri/gomulu-hedefler

Standart kütüphanenin temelindeki işletim sistemi hizmetlerinin (yazdırma, bellek ayırma, panik işleme) hiç bulunmadığı bir hedefin kaynakta nasıl görünür kılındığı; aynı işi bir kez ayırma yaparak, bir kez sabit boyutlu bir tamponla yazan iki gövdenin aynı sonucu verdiği koşturularak gösterilir.

Önceki ders, aynı işletim sisteminin iki ayrı hedefte farklı hizmetler sunduğunu ölçtü:
dosya sistemi ve iş parçacığı, tarayıcı hedefinde ya hiç yoktu ya da farklı bir yoldan
geliyordu. Bu ders sınırı bir uca taşıyor — işletim sisteminin **hiç bulunmadığı** bir
hedef. Böyle bir hedefte yalnız birkaç hizmet değil, standart kütüphanenin dayandığı
temel bile değişir: yazdırma, bellek ayırma ve panik işleme, üçü de bir işletim
sisteminin varlığını varsayar.

Bu tür bir hedef, tipik olarak sınırlı belleğe sahip ve kendi başına bir işletim
sistemi çalıştırmayan bir donanımı temsil eder: bir sensör kartı, bir motor
denetleyicisi, bir kartlı ödeme cihazı. Böyle bir donanımda kod doğrudan işlemcide
koşar; altında dosya sistemini yöneten, bellek ayırmasını düzenleyen ya da bir program
çöktüğünde iletiyi bir uçbirime basan bir katman yoktur. Rust bu ortam için standart
kütüphanenin bütününü değil, yalnız çekirdek dili ve donanımdan bağımsız temel tipleri
(tam sayılar, dilimler, seçenek ve sonuç tipleri gibi) sağlayan daha dar bir kütüphane
sunar; dosya, iş parçacığı, ağ ve bellek ayırma gibi işletim sistemine dayanan parçalar
bu dar kütüphanenin dışında kalır. Bu ayrım kaynakta bir bildirimle işaretlenir: bu ders
o bildirimi kullanmıyor, çünkü kullanan kaynak bu makinede sıradan bir program olarak
çalıştırılamaz — bildirim, standart kütüphanenin bütününü isteyen hiçbir çağrının
kalmadığını, aksi hâlde derlemenin duracağını söyler. Bu derste ölçülen ayrım, o
bildirim yazılmasa bile kaynaktan okunabilir: hangi gövdenin hangi hizmete dayandığı,
kullandığı çağrıdan bellidir.

Bu derste de gömülü bir hedef için gerçek bir derleme yapılmaz. Hedefe bağlı karar,
önceki derste olduğu gibi kaynak üzerinden modellenir: kaynaktaki iki gövdeden hangisinin
seçileceğini `cfg` belirler ve derlemeye yalnız biri girer.

#### Yazdırmanın Bir İşletim Sistemi Hizmeti Olması

Bir yazdırma çağrısı, arkasında bir işletim sistemi çağrısı çalıştırır: metni bir akışa
yazar, akış işletim sistemi tarafından bir uçbirime ya da bir dosyaya yönlendirilir. Bu
zincirin ilk halkası — işletim sisteminin kendisi — bulunmayan bir hedefte, aynı çağrı
hiçbir yere yazamaz. Önceki derste bir hizmetin (dosya sistemi) yokluğu bir hata değeri
olarak geri dönüyordu — program yine de derleniyor, çalışma zamanında bir `Result`
üzerinden anlaşılıyordu. Yazdırma çağrısının burada durumu farklı: standart kütüphanenin
dar sürümünde bu çağrının kendisi hiç yoktur, bu yüzden yokluk bir çalışma zamanı
sonucu değil, bir **derleme zamanı** sonucudur — çağrıyı içeren kaynak, o hedef için
hiç derlenmez. Aşağıdaki koşum bu bağımlılığı `cfg` ile işaretliyor.

**DS15.** Yazdırılan ileti `"olcum tamamlandi"` — önceki dersteki sensör kaydının işlendiği
varsayılan bir aşamanın bittiğini bildiriyor.

```rust
// gomulu1.rs — yazdirma hizmetinin isletim sistemine dayali olmasi
#[cfg(not(target_os = "none"))]
fn bilgi_yaz(mesaj: &str) {
    // Standart cikis akisina yazma: isletim sisteminin sagladigi bir hizmet.
    println!("{}", mesaj);
}

#[cfg(target_os = "none")]
fn bilgi_yaz(mesaj: &str) {
    govde_gomulu_hedefte_baska_bir_yontemle_yazar(mesaj)
}

fn main() {
    bilgi_yaz("olcum tamamlandi");
}
```

```
olcum tamamlandi
```

`target_os = "none"`, işletim sistemi bulunmayan hedeflerin taşıdığı değerdir; bu makine
bir işletim sistemi altında koştuğu için bu koşulu sağlamaz ve `not(target_os = "none")`
işaretli gövde derlemeye girer. Karşıt gövde, önceki dersteki wasm örneğinde olduğu gibi,
hiç derlenmeden kaynakta duruyor — içindeki tanımsız çağrı bu yüzden hiçbir soruna yol
açmıyor. **DS16.** Gerçek bir işletim sistemi bulunmayan bir hedefte bu ikinci gövde
yazdırma işini görebilmesi için bir donanım kaydına doğrudan yazmak ya da bir hata
ayıklama arayüzüne veri göndermek gibi tamamen farklı bir yönteme ihtiyaç duyar — bu
yöntem işletim sisteminin sağladığı akış soyutlamasından bağımsızdır.

`target_os = "none"`, önceki derste kullanılan `target_family = "wasm"`dan farklı bir
soruyu sorar. `target_family`, bir hedefin geniş bir ortam sınıfına (wasm ailesi, unix
ailesi gibi) ait olup olmadığını sorar; `target_os` ise daha dar bir soru sorar ve
hedefin **hangi** işletim sistemi üzerinde koştuğunu, ya da hiç koşmadığını adlandırır.
`"none"` değeri, bu sorunun yanıtının "hiçbiri" olduğu tek durumdur — hedefin altında
dosya sistemi, süreç yönetimi ya da akış sunan bir katman yoktur. Aynı öznitelik başka
değerler de taşıyabilir (bir işletim sisteminin adı gibi), ama bu ders yalnız "hiç
işletim sistemi yok" durumuyla ilgileniyor.

Bu iki gövde arasındaki fark, önceki dersteki dosya sistemi örneğiyle aynı biçimde
kuruldu, ama dayandığı hizmet farklı. Orada eksik olan tek bir çağrıydı; burada eksik
olan, standart kütüphanenin **her** yazdırma çağrısının temelinde duran bir akış
soyutlamasının kendisi. Yazdırma çağrısı, aslında bir dizi katmanın en üstünde duruyor:
metni biçimlendirir, sonucu standart çıkış akışına yazan bir işleve devreder, o işlev de
altında işletim sisteminin sunduğu bir yazma çağrısını kullanır. Bu zincirin son halkası
yoksa, zincirin geri kalanı da anlamsızlaşır — bu yüzden bu yazdırma yolu, işletim
sistemi bulunmayan bir hedef için hazırlanan dar kütüphanede doğrudan yer almaz.

#### Ayırma Yapan ve Ayırma Yapmayan Aynı İş

Bellek ayırma da benzer bir bağımlılık taşır, ama farkı `cfg` ile değil, iki farklı
yazımla göstermek daha açık: her iki yazım da bu makinede derlenip koşuyor, aralarındaki
fark yalnızca birinin **bellek ayırdığı**, ötekinin ayırmadığı. Yazdırma örneğinde iki
gövdeden yalnız biri derleniyordu; burada ikisi de derleniyor ve ikisi de çalıştırılıyor,
çünkü ölçülen şey hangisinin derlendiği değil, aynı sonucu **hangi kaynağa dayanarak**
ürettikleri. Aşağıdaki koşum aynı sensör kaydını iki ayrı yolla topluyor.

```rust
// gomulu2.rs — ayirma yapan ve ayirma yapmayan govdenin ayni sonucu vermesi
fn ayirarak_topla(kayitlar: &[i32]) -> i32 {
    let mut biriktirici: Vec<i32> = Vec::new();
    for &deger in kayitlar {
        biriktirici.push(deger);
    }
    biriktirici.iter().sum()
}

fn tampon_ile_topla(kayitlar: &[i32]) -> i32 {
    const KAPASITE: usize = 8;
    let mut tampon = [0i32; KAPASITE];
    let mut dolu = 0usize;
    for &deger in kayitlar {
        tampon[dolu] = deger;
        dolu += 1;
    }
    tampon[..dolu].iter().sum()
}

fn main() {
    let kayitlar = [12, 7, 5, 21];
    println!("ayirarak toplam: {}", ayirarak_topla(&kayitlar));
    println!("tampon ile toplam: {}", tampon_ile_topla(&kayitlar));
}
```

```
ayirarak toplam: 45
tampon ile toplam: 45
```

**DS17.** İki toplam da `45` — aynı dört sayı, aynı sonuç. `ayirarak_topla`, her
`push` çağrısında büyüyen dizinin kapasitesini gerektiği kadar genişletiyor; bu genişleme
bir bellek ayırıcıdan yeni bir blok istemekle sonuçlanır ve ayırıcı, çalışma zamanının
bir parçasıdır. `tampon_ile_topla` ise hiçbir ayırıcıya dokunmuyor: `tampon` dizisinin
boyutu kaynakta `KAPASITE` sabitiyle önceden belirlenmiş, dizi bir işlev çağrısı kadar
kısa ömürlü ve bellekteki yeri derleme zamanında bilinen bir alanda duruyor.

Bir bellek ayırıcı bulunmayan bir hedefte `ayirarak_topla` derlenemez ya da derlense
bile çalışma zamanında ayırma isteği karşılanamadığı için ilerleyemez; `tampon_ile_topla`
ise böyle bir hedefte de aynı sonucu üretir, çünkü hiçbir dış kaynağa ihtiyaç duymaz.
Bu fark koşumda görünmüyor — ikisi de bu makinede sorunsuz çalıştı — yalnızca kaynaktaki
`Vec` ile sabit boyutlu dizi arasındaki seçimde görünüyor.

Bu tercih yalnız bir ayırıcının var olup olmamasıyla da sınırlı değil. Bir sensör kartı
gibi az bellekli bir donanımda, bir programın en fazla ne kadar bellek kullanacağının
**önceden** bilinmesi ayrı bir değer taşır: `tampon`ın boyutu kaynakta sabit olduğu için,
bu işlevin kullanacağı bellek miktarı derleme anında bellidir ve hiçbir çalışma anı
kararına bağlı değildir. `Vec` tabanlı yazımda kullanılan bellek, gelen kayıt sayısına
göre değişir; kayıt sayısı önceden bilinmiyorsa, kullanılacak üst sınır da önceden
bilinmez. Sabit boyutlu bir tamponun getirdiği kısıt — üst sınırın kaynakta yazılı
olması — aynı zamanda bu öngörülebilirliğin kaynağıdır.

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

Sabit boyutlu tamponun bu işi görebilmesinin bir koşulu var: `KAPASITE`, gelen kayıt
sayısından **büyük ya da eşit** olmalı. Yukarıdaki koşumda dört kayıt sekiz kapasitelik
bir tampona sığdı. Kayıt sayısı kapasiteyi aşsaydı ve kod bunu denetlemeseydi,
`tampon[dolu] = deger` satırı dizinin sınırlarının dışına yazmaya çalışırdı — bu, Rust'ta
sınır denetimi her zaman çalıştığı için sessizce geçmez, program çalışma zamanında
dururdu. Bu ders panik tetiklemediği için o durumu koşturmuyor; onun yerine, aynı sınırı
panik atmadan ele alan bir yazım koşturuyor.

**DS18.** On kayıt gönderiliyor, kapasite sekiz; ilk sekizi kabul edilip toplanıyor, son
ikisi reddediliyor.

```rust
// gomulu3.rs — kapasiteyi asan girdiyi panik atmadan reddeden tampon
fn tampon_ile_topla_guvenli(kayitlar: &[i32]) -> (i32, usize) {
    const KAPASITE: usize = 8;
    let mut tampon = [0i32; KAPASITE];
    let mut dolu = 0usize;
    let mut reddedilen = 0usize;
    for &deger in kayitlar {
        if dolu < KAPASITE {
            tampon[dolu] = deger;
            dolu += 1;
        } else {
            reddedilen += 1;
        }
    }
    (tampon[..dolu].iter().sum(), reddedilen)
}

fn main() {
    let cok_kayit = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let (toplam, reddedilen) = tampon_ile_topla_guvenli(&cok_kayit);
    println!("kabul edilen toplam: {} reddedilen kayit: {}", toplam, reddedilen);
}
```

```
kabul edilen toplam: 36 reddedilen kayit: 2
```

Bu yazım, `dolu < KAPASITE` denetimini yazmayla aynı işlemi birleştirerek sınırı asla
aşmıyor — dokuzuncu ve onuncu kayıt, tampona hiç yazılmadan sayılıp bir kenara
bırakılıyor. Sonuç, panik yerine bir bilgi: kaç kaydın kabul edildiği, kaç kaydın
reddedildiği. **DS19.** Bu, gömülü bir hedefte yaygın bir alışkanlığı gösteriyor: bir
işletim sistemi çökeni yakalayıp iletiyi yazdıramadığı için, sınır denetimini önceden
elle yapıp durumu bir sonuç değeriyle bildirmek, panikle durmaktan daha güvenilir bir
yazım kabul edilir — panik sonrası davranış hedefe göre değiştiği hâlde, bir sonuç
değeri her hedefte aynı biçimde ele alınabilir.

`ayirarak_topla` böyle bir sınır taşımıyor — `Vec`, kapasitesi dolduğunda kendini
büyütüyor, kayıt sayısı önceden bilinmese de çalışıyor. Bu, iki yazım arasındaki gerçek
değiş tokuşu gösteriyor: sabit boyutlu tampon, hedefin bir ayırıcı sunmasını
gerektirmiyor, ama üst sınırı kaynakta yazılı olmak zorunda ve o sınır aşıldığında ya
durur ya da (yukarıdaki gibi elle yazılmışsa) fazlasını reddeder. Büyüyen dizi ise üst
sınır taşımıyor, ama bir ayırıcı bulunmasını şart koşuyor. Ayırmasız bir yazım her zaman
mümkün değildir; kayıt sayısı çalışma zamanında öğrenilen ve önceden sınırlanamayan bir
işte, sabit boyutlu bir tampon fazlasını ya reddetmek ya da durmak zorunda kalır.

Gerçek bir gömülü sistemde `KAPASITE` genellikle keyfi seçilmez; donanımın kendisinden
gelen bir sayıya bağlanır — bir kartta kaç sensör kanalı olduğu, bir iletişim
çerçevesinin en fazla kaç bayt taşıdığı, bir arabelleğin sığdığı bellek bölgesinin
boyutu gibi. Bu ders için seçilen `8` böyle bir donanım kısıtını temsil ediyor; on
kayıtlık girdi, o kısıtın aşıldığı bir durumu göstermek için seçildi. Kod, aştığı
girdiyi sessizce yok saymak yerine kaçının reddedildiğini sayarak bildiriyor — bu da
gömülü yazılımda sık görülen bir başka alışkanlık: bir sınırın aşıldığını fark etmeden
geçmek, sınırın aşıldığını fark edip bunu bir yerde kaydetmekten daha kötü bir sonuçtur.

Panik davranışının kendisi de bu sınırın bir parçası. Bir işletim sistemi altında panik,
bir ileti yazdırıp programı durdurur; bazı yapılandırmalarda program bu noktadan geri
sarılmayı da dener, bazılarında doğrudan durur — ama iki durumda da bu davranış bir
işletim sisteminin sağladığı süreç yönetimine dayanır. İleti bir akışa yazılır, süreç
işletim sistemine bir çıkış koduyla döner, işletim sistemi bu süreci kapatır. İşletim
sistemi bulunmayan bir hedefte bu zincirin hiçbir halkası yoktur; standart kütüphanenin
dar sürümü, panik anında hangi davranışın izleneceğini **belirtmez**, yalnızca bu
davranışı kaynağın ayrıca tanımlamasını zorunlu kılar. Yazan taraf bu tanımda bir donanım
kaydına sabit bir değer yazmayı, işlemciyi düşük güç kipinde sonsuz bir döngüye
sokmayı ya da bir hata ayıklama arayüzüne bir işaret göndermeyi seçebilir; hangisinin
seçildiği, donanımın kendisine ve o donanımın hangi geri kazanım yollarını sunduğuna
bağlıdır. Bu ders böyle bir tanım koşturmuyor, yalnızca varlığını anıyor — yazdırma ve
ayırma ile aynı sınıfa ait bir bağımlılık olduğu için.

Üç bağımlılığın (yazdırma, ayırma, panik işleme) ortak noktası, hiçbirinin dilin
kendisinden **eksik** olmamasıdır. Rust bu üçünü de dil düzeyinde tanır; yazdırma
sözdizimi, `Vec` tipi, panik sözcüğü, hepsi çekirdek dilin ya da standart kütüphanenin
bir parçasıdır. Eksik olan, bu üçünün **çalışabilmesi için gereken ortamdır** — bir
akış, bir ayırıcı, bir süreç yönetimi. Dil aynı kalıyor, hedefin sunduğu ortam
değişiyor; bu yüzden bu üç bağımlılığın sınıfı **dilin dışında**, derleyicinin ya da
standart kütüphanenin bir eksikliği değil.

#### Özet

- Yazdırma, bellek ayırma ve panik işleme, standart kütüphanenin işletim sistemine
  dayanan üç temel hizmeti; işletim sistemi bulunmayan (`target_os = "none"`) bir
  hedefte üçü de kaynakta ayrıca ele alınmak zorunda
- Gömülü bir hedef için gerçek bir derleme yapılmadı; hedefe bağlı karar `cfg` ile
  kaynak üzerinden **modellendi**
- Ayırma yapan gövde (`Vec`) ile sabit boyutlu bir tamponla çalışan gövde aynı toplamı
  (`45`) verdi; fark sonuçta değil, birinin bir ayırıcı gerektirmesinde
- Bir hedefin bellek ayırıcı, işletim sistemi hizmeti ya da panik işleyicisi sunmaması,
  bu hedefin verdiği güvencenin dilin kendisinden değil hedeften geldiğini gösterir —
  sınıf **dilin dışında**
- Sabit boyutlu bir tampon ayırıcı gerektirmez ama üst sınırı kaynakta yazılı olmak
  zorundadır; bu sınır aşıldığında ayırmasız yazım ya durur ya da (sınır elle
  denetlenmişse) fazlasını panik atmadan reddeder

#### Sonraki Adım

Bu konu boyunca güvence hep aynı yerde bitti: derleyicinin denetimi değil, dilin
dışındaki bir düzenek belirleyiciydi. `unsafe` Rust dersinde bu düzenek yazana devredilen
bir sözleşmeydi; Yabancı Fonksiyon Arayüzü dersinde derleyicinin hiç görmediği bir dilin
imzasıydı; WebAssembly Hedefi dersinde tarayıcının yalıttığı bir ortamdı; bu derste ise
işletim sisteminin kendisinin yokluğuydu. Dördünde de ortak olan, güvencenin sınırının
**kaynaktan okunabilir** olması — `unsafe` bloğu, `extern` bildirimi ve `cfg` etiketi,
üçü de bu sınırı kaynakta işaretleyen birer iz; hiçbiri koşum sırasında keşfedilmesi
gereken gizli bir davranış değil. Sıradaki konu bu eksenden ayrılıyor:
orada sorulan, bir bağımlılığın ya da
bir sınamanın **hangi güvenceyi verdiği**, bir hedefin neyi elden aldığı değil.

## Proje Yaşam Döngüsü

Koşullu derlemeyle modüler bir kitaplık kurmak, birim ve tümleşim sınamalarıyla rastgele girdi üretiminin verdiği güvence, başarım iddialarının sayıyla doğrulanması, kaynaktan üretilen belgenin sınanabilir örnekleri, ve bir sürümün ne söz verdiği.

### Bağımlılık ve Özellik Bayrakları

Source: https://academia.sh/tr/kurslar/rust-ileri/bagimlilik-ve-ozellik-bayraklari

Koşullu derlemeyle dışarıda bırakılan bir kaynak parçasının derleyici tarafından hiç görülmediğinin, bu gözlemin iki ayrı özellik bayrağıyla tekrarlanarak sınıfının (denetlenmiyor) doğrulanması.

Alt Düzey konusu dört dersle kapandı ve dördünde de derleyici en azından bir şeyi
görüyordu: bir `unsafe` bloğun içindeki birkaç işlem denetimsiz kalıyordu ama aynı
bloktaki başka bir işlem hâlâ denetleniyordu; yabancı bir işlevin gövdesi hiç
görülmüyordu ama en azından imzası derlemeye giriyordu; bir hedefin verdiği sınır
kaynaktan okunuyordu, standart kütüphaneye dayanan ve dayanmayan iki yazım yerel
hedefte yan yana koşturulmuştu. Dördünde de derleyici kaynağın *bir kısmını*
derliyor, güvence oradan başlıyordu — denetim daraldı, ama tümüyle durmadı. Proje
Yaşam Döngüsü konusu bambaşka bir soru soruyor: kaynakta duran ama derleyicinin hiç
görmediği bir parça var mı ve varsa bu parça bozulsa bile kimse fark eder mi?

Bu derste yanıt koşullu derlemede aranıyor. Bir kaynak dosyasının bir bölümü, bir
özellik bayrağının açık olup olmamasına göre derlemeye girer ya da hiç girmez;
girmeyen bölüm, sözdiziminin bile geçerli olması dışında hiçbir denetimden geçmez.
Rust Ekosistemi ve İleri Konular kursunun ortak sorusu — derleyicinin güvencesi burada
nerede bitiyor — bu derste en keskin yanıtını buluyor: bazı gövdeler için güvence hiç
başlamıyor.

Kursun dört yanıtı — güvencenin **sürdüğü**, **yazana devredildiği**, **dilin
dışında** kaldığı ve **hiç kimse tarafından denetlenmediği** durumlar — Proje Yaşam
Döngüsü konusunun dört dersinde de tek tek karşılığını buluyor: bu ders bir gövdenin
hiç denetlenmediğini, sıradaki iki ders bir sınamanın ya da bir sayının hangi
güvenceyi *koşarak* kurduğunu, son ders ise aynı kaynakta hem hiç denetlenmeyen hem
de gerçekten denetlenen iki iddianın yan yana durabileceğini gösteriyor.

#### Kapsam Dışı Kalan Gövde

Bir bildirim dosyası, normal koşullarda bir kasanın hangi bölümlerinin isteğe bağlı
olduğunu özellik bayraklarıyla tanımlar; paket yöneticisi bu bayrakları derleyiciye
`--cfg` seçeneğiyle iletir. Bu kursta paket yöneticisi kullanılmadığından bayrak durumu
doğrudan derleyiciye verilir, ama düzenek aynıdır: `#[cfg(feature = "ad")]`
niteliğiyle işaretlenmiş bir öğe, yalnız o bayrak açıkken derlemeye girer. Bayrak
kapalıyken derleyici o öğeyi **hiç görmez** — ne tip denetiminden geçirir ne de adları
çözer.

Bağımlılıklarla özellik bayrakları çoğunlukla birlikte anılır çünkü bir bayrak
sıklıkla bir bağımlılığa duyulan ihtiyacı sınırlamak için kullanılır: bir bölüm
belirli bir dış işlevselliğe dayanıyorsa ve bu işlevselliğe çoğu kullanıcı ihtiyaç
duymuyorsa, o bölüm bir bayrağın arkasına konur ve bayrak kapalıyken hem o bölüm hem
de dayandığı bağımlılık derlemeye hiç girmez. Bu derste kasa dışı bir bağımlılık
kullanılmıyor — kurs boyunca bağımlılıklar standart kütüphaneyle sınırlı — ama
mekanizma birebir aynı: `#[cfg(...)]` bir öğeyi neye dayandığına bakmaksızın
derlemenin dışında bırakır.

Özellik bayrakları çoğunlukla bir kasanın isteğe bağlı bölümlerini ayırmak için
kullanılır: bir bölüm çoğu kullanıcıya gerekmiyorsa bir bayrağın arkasına konur ve
yalnız isteyen taraf o bayrağı açar. Bir bildirim dosyası bu bayrakların hangisinin
**varsayılan** olarak açık geleceğini de belirtir; açıkça kapatılmadıkça varsayılan
bayraklar her derlemeye girer, geri kalanı yalnız isteyen tarafın elini uzatmasıyla
açılır. Bunun getirisi, kullanılmayan bölümün hiç derlenmemesidir — ama bu
derste ölçülen getiri değil, bu getirinin **bedeli**: kapalı kalan bölüm, açık kalan
bölümden farklı bir denetim düzeyinde yaşıyor.

Aşağıdaki kesit iki bayrakla işaretlenmiş iki gövde taşıyor. İlki, `acilmayan` bayrağı
altında duruyor ve iki farklı hata taşıyor: var olmayan bir işlevi çağırıyor ve bir
dizgeyi doğrudan bir tam sayıya atıyor. İkincisi, `denenmemis` bayrağı altında duruyor
ve o da benzer biçimde var olmayan bir yöntemi çağırıp sonucunu yanlış bir tipe
atıyor. İki gövde de bilerek iki farklı statik denetim türünü ihlal edecek biçimde
kuruldu: biri **ad çözümlemesini** (var olmayan bir çağrı), öbürü **tip
denetimini** (yanlış bir atama) ihlal ediyor. İkisinin de aynı sonuca — sessizce
dışarıda kalmaya — vardığını göstermek için ikisi de gerekliydi; yalnız bir tür
hata seçilseydi gözlem yalnız o hata sınıfına özgü olabilirdi. Üçüncü gövde
hiçbir bayrağa bağlı değil ve her koşulda derlemeye giriyor.

```rust
// bayrak_govdeleri.rs — iki ayri, hep kapali kalan bayrak, hep acik kalan bir govde
#[cfg(feature = "acilmayan")]
pub fn hic_denetlenmeyen() {
    bu_islev_hic_tanimlanmadi();
    let _x: i32 = "metin";
}

#[cfg(feature = "denenmemis")]
pub fn baska_denetlenmeyen(liste: &[i32]) -> i32 {
    let toplam: i32 = liste.paralel_topla();
    let _s: &str = toplam;
    toplam
}

pub fn calisan_govde() -> i32 {
    41
}
```

Bu dosya hiç `fn main` taşımıyor; bir kaynak kesiti olarak duruyor ve aşağıdaki gövde
onu `mod` bildirimiyle çekiyor. **PY1.** Buradaki iddia şu: hiçbir bayrak açık
olmadığında, `bayrak_govdeleri.rs` içindeki iki hatalı gövde kaynakta dursa bile
derleme **uyarısız** tamamlanır — çünkü ikisi de derlemeye hiç girmiyor.

```rust
// bayrak_olcumu.rs — iki bayrak da kapali; disarida kalan iki govde hic denetlenmiyor
mod bayrak_govdeleri;
use bayrak_govdeleri::calisan_govde;

fn main() {
    println!("calisan govde sonucu: {}", calisan_govde());
    println!("acilmayan bayrak acik mi: {}", cfg!(feature = "acilmayan"));
    println!("denenmemis bayrak acik mi: {}", cfg!(feature = "denenmemis"));
}
```

```
calisan govde sonucu: 41
acilmayan bayrak acik mi: false
denenmemis bayrak acik mi: false
```

Derleme hiçbir uyarı basmadı ve program üç satır yazdı. `hic_denetlenmeyen` ile
`baska_denetlenmeyen`in ikisi de kaynakta duruyor, ikisinin de içinde tanımsız bir çağrı
ve bir tip hatası var ve ikisi de bu koşumda **hiç derlenmedi**. Derleyici onları
görmediği için ne çağrıyı ne de hatalı atamayı fark edebildi. Kod içindeki bir
çalışma-zamanı bayrak sorgusu bu durumu program içinden doğruluyor: her iki bayrak da
koşum sırasında kapalı okunuyor — ikisi de kapalı, ikisi de gövdesiz.

Bu, bir bayrağı sıradan bir çalışma zamanı koşuluyla karıştırmamak için önemli bir
ayrım taşıyor. Aynı iki hatalı gövde `#[cfg(...)]` yerine sıradan bir çalışma zamanı
dallanmasının (örneğin hiçbir zaman doğru olmayan bir koşulun) içine yazılsaydı sonuç
aynı olmazdı: böyle bir dallanmanın gövdesi çalışma zamanında hiç işletilmese bile,
kaynak ağacında hâlâ bulunduğu için derleme zamanında **tip denetiminden geçer**.
Tanımsız bir çağrı orada yazılı olsaydı derleme başarısız olurdu — çalışma zamanında
hiç ulaşılamayan bir dal, tanımsız bir adı gizlemez. `#[cfg(...)]` bunun tam tersini
yapar: gövdeyi kaynak ağacından, dolayısıyla adları çözme ve tip denetleme
aşamalarından tamamen çıkarır. **PY2.** Bu fark, "hiç çalışmayan kod" ile "hiç
derlenmeyen kod"un aynı şey olmadığını gösteriyor; yalnız ikincisi bu dersin
sınıfına, denetlenmiyor sınıfına giriyor.

**Ayrım cümlesi:** M09/K05 kursu derleme etiketlerini bir programın **araç
bilgisi** olarak ölçtü — bir derleme etiketi hangi dosyanın hangi işletim sistemine
ya da mimariye derlendiğini belirliyordu ve bunu söyleyen bir araçtı. Bu derste
ölçülen bambaşka: dışarıda kalan gövdenin **hiç denetlenmediği**. Orada bir bilgi
kaynaktan mı, araçtan mı, koşumdan mı geliyordu diye soruluyordu; burada sorulan,
kaynağın bir kısmının derleyicinin ilgi alanına hiç girip girmediği. İki kavram da
`#[cfg(...)]` niteliğini kullanıyor — Rust'ta hedef seçimi de özellik seçimi de aynı
mekanizmadan geçiyor — ama sordukları soru farklı: biri "hangi dosya nereye
derlenir", öbürü "derlenmeyen bir gövdeyi kim denetler".

Aynı gözlem ikinci bir bayrakla (`denenmemis`) tekrarlandı çünkü tek bir bayrağın
kapalı kalması bir rastlantı olabilirdi — belki o bayrak zaten hiç kullanılmayacak
biçimde tasarlanmıştı. **PY3.** İki farklı bayrak, iki farklı hata türü (tanımsız
çağrı ile yanlış tip ataması, sonra tanımsız yöntem ile yanlış tip ataması)
taşıyarak aynı sonuca ulaştı: kapalı bir bayrağın gövdesi, içeriği ne olursa olsun,
derleme sürecine hiç girmiyor. Bu, tek bir örneğin özel bir durumu değil, koşullu
derlemenin genel davranışı.

Bir proje bu riski, her bayrak bileşimini ayrı ayrı derleyen bir sürece devrederek
azaltabilir: her bileşim en az bir kez derlenirse, o an için hiçbir bayrak kapalı
kalmaz ve denetim geçici olarak tam kapsama ulaşır. Bu, ölçülen sınıfı
**denetlenmiyor**dan çıkarmaz — yalnızca denetlenme anını, bayrağın açıldığı ana
öteler. Böyle bir süreç çalıştırılmadığı her an, kaynak ağacındaki kapalı bayrakların
gövdeleri yine denetimsiz kalmaya devam eder; bu dersin ölçtüğü durum, o sürecin
**yokluğunda** neyin olduğudur, varlığında neyin garanti edildiği değil.

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

Yukarıdaki koşum, kapalı bir bayrağın gövdesinin derlemeye girmediğini gösterdi. Ama
bu, o gövdenin *görünmez* olduğu anlamına gelmiyor — kaynakta duruyor, okunabiliyor,
sürüm denetim sisteminde izlenebiliyor. Görünmeyen olan, derleyicinin ona bakıp
bakmadığı. Aşağıdaki koşum bu ayrımı çalışan bir programın içinden gösteriyor:
kapalı bayrağın gövdesine ayrılmış bir blok, programın kendisi tarafından da hiç
işletilmiyor.

```rust
// govde_gorunurlugu.rs — kapali bir bayragin govdesi kosumdan da gorunmez
mod bayrak_govdeleri;
use bayrak_govdeleri::calisan_govde;

fn main() {
    let sonuc = calisan_govde();
    #[cfg(feature = "acilmayan")]
    {
        println!("bu satir hicbir zaman derlenmiyor");
    }
    #[cfg(not(feature = "acilmayan"))]
    {
        println!("acilmayan bayrak kapali: govdesi kaynakta duruyor, kosuma hic girmedi");
    }
    println!("calisan govde yine {} dondurdu", sonuc);
}
```

```
acilmayan bayrak kapali: govdesi kaynakta duruyor, kosuma hic girmedi
calisan govde yine 41 dondurdu
```

**PY4.** İki blok da aynı `main` işlevinin içinde yazılı, ama yalnız biri koşuma
giriyor — hangisinin gireceğine derleme anında, `cfg` niteliğine bakılarak karar
veriliyor. Kaynakta iki blok da var; koşumda yalnız biri var. Bu, koşullu
derlemenin okuyucuya verdiği bir söz değil, kaynağın kendisinden çıkarılabilecek bir
gerçek — ama bu ölçümün kendisi bir sınır taşıyor.

Bu koşumda `#[cfg(...)]` bir öğenin (bir işlevin) üzerinde değil, `main` içindeki bir
ifade bloğunun üzerinde duruyor; niteliğin kendisi hem işlevlere hem yapılara hem de
tek bir ifade bloğuna uygulanabilir. Gerçek bir projede bu, bir işlevin çoğu paylaşılan
kalırken yalnız küçük bir bölümünün — örneğin bir günlükleme çağrısının ya da bir
ek denetimin — bir bayrağın arkasına konabileceği anlamına gelir; işlevin tamamını
bayrağa bağlamak zorunlu değildir. Ölçülen sınıf değişmez: hangi düzeyde uygulanırsa
uygulansın, kapalı kalan blok yine derlemeye hiç girmez.

Yukarıdaki koşumda `#[cfg(feature = "acilmayan")]` ile `#[cfg(not(feature =
"acilmayan"))]` **aynı yerde**, birbirini dışlayan iki blok olarak yazıldı. Bu,
bayrakların sık kullanılan bir başka biçimi: bir bayrak açıkken bir davranış, kapalıyken
onun karşıtı bir davranış çalışır ve kaynakta ikisi de görünür durur. Derleyici
bunlardan yalnız bayrak durumuna uyanı derlemeye alır, öbürünü tıpkı yukarıdaki
`hic_denetlenmeyen` gibi tamamen dışarıda bırakır — kapalı kalan taraf burada hatalı
değil, yalnızca kapalı; ama derleyicinin ona bakmaması gerçeği değişmiyor.

Bu ölçüm, kapalı bir bayrağın gövdesinin *bugün* derlenmediğini gösteriyor; gövdenin
bayrak açıldığında derlenip **derlenmeyeceğini** göstermiyor. Bunu göstermenin tek
yolu bayrağı gerçekten açıp derlemeyi denemek, ama `hic_denetlenmeyen` ile
`baska_denetlenmeyen` açıldığında derlenmeyecek biçimde kurulmuş durumda —
derlenmeyen bir kaynağı bu derste koşturmak mümkün değil. Bu yüzden ölçüm yalnızca
şunu kanıtlıyor: **kapalı bir bayrağın gövdesi bugün kimse tarafından
denetlenmiyor.**

Bunu sıradan kullanılmayan bir öğeyle karşılaştırmak ayrımı netleştiriyor. Bir işlev
hiçbir bayrağa bağlı olmadan yazılır ama hiç çağrılmazsa, derleyici bunu fark eder ve
bir kullanılmama uyarısı basar — çünkü o işlev derleme sürecine girmiştir, adı
çözülmüş ve tipi denetlenmiştir, yalnız çağrılmamıştır. Kapalı bir bayrağın gövdesi bu
uyarıyı hiç almaz, çünkü derleme sürecine hiç girmemiştir; sessizliğin nedeni
"kullanılmadığı bilinip görmezden gelinmesi" değil, "hiç var olmamış gibi
davranılmasıdır". Bu derste teslim edilen her kod bloğu uyarısız derlendiği için bu
karşılaştırma koşulmuyor, ama derleyicinin kullanılmayan koda verdiği tepkiyle kapalı
bir bayrağın gövdesine verdiği (vermediği) tepki arasındaki fark, sınıfın
(denetlenmiyor) neden yalnızca kapalı bayraklara özgü olduğunu açıklıyor.

Açıldığında ne olacağı, açılana kadar bilinmiyor — ve bu
bilinmezlik, kapalı bir bayrağın gövdesinin zamanla kaynaktaki geri kalanla
uyumunu yitirmesinin (çürümesinin) doğrudan nedeni. Kaynak ağacı büyüdükçe,
etrafındaki tipler ve işlev imzaları değiştikçe, kapalı bir bayrağın gövdesi bu
değişikliklerin hiçbirine ayak uydurmak zorunda kalmıyor; derleyici onu kontrol
etmediği için uyumsuzluk yalnızca bayrak açıldığı gün ortaya çıkıyor.

Bu sınır tek bir bayrakla da bitmiyor. Bir kaynak ağacı yalnız iki bağımsız bayrak
taşısa bile açık/kapalı ikilisi dört olası derleme durumu üretir; bayrak sayısı
arttıkça olası durum sayısı katlanarak büyür. Alışılmış bir derleme akışı bu
durumların yalnızca birkaçını — çoğunlukla hepsi kapalı ya da hepsi açık olan uç
durumları — gerçekten derler; aradaki bileşimlerin çoğu hiç denenmez. Yukarıdaki
koşum tek bir kaynak dosyasında iki bayrağı da kapalı bırakarak bu uç durumlardan
birini gösterdi; `acilmayan` ile `denenmemis`in **birlikte** açıldığı bir durum bu
derste hiç kurulmadı, çünkü böyle bir durum bu iki gövdenin ikisini de derlemeye
sokar ve ikisi de derlenmeyecek biçimde kurulmuştur. Denenmeyen bileşimlerin sayısı
arttıkça, kapalı kalan bir bayrağın ne zaman açılacağını ve o an ne olacağını
kestirmek de zorlaşır. Bu derste yalnızca iki bayrak, dört olası bileşimin en uç
olanı (ikisi de kapalı) gösterildi; kalan üç bileşim — yalnız `acilmayan` açık, yalnız
`denenmemis` açık, ikisi birden açık — bu ölçümün kapsamı dışında kaldı ve bu kapsam
dışılığın kendisi de, ölçülen sınıfın (denetlenmiyor) bir başka örneği.

#### Özet

- `#[cfg(feature = "ad")]` ile işaretlenmiş bir öğe, o bayrak kapalıyken derleyiciye
  hiç girmez; tanımsız çağrı ve tip hatası taşısa bile derleme uyarısız tamamlanır.
- Bu gözlem iki farklı bayrakla (`acilmayan`, `denenmemis`) ve iki farklı hata
  türüyle tekrarlandı; sonuç ikisinde de aynı kaldı.
- Güvencenin sınıfı **denetlenmiyor**: kapalı bir bayrağın gövdesini ne derleyici, ne
  bir araç, ne de koşan program denetliyor.
- Sıradan bir çalışma zamanı dallanması (`if` gibi) gövdeyi yalnız çalışma yolundan
  çıkarır ve gövde yine de tip denetiminden geçer; `#[cfg(...)]` gövdeyi kaynak
  ağacından çıkarır ve tip denetimi hiç başlamaz — iki dışlama biçimi aynı şey
  değildir.
- Bu ölçümün sınırı: kapalı bir bayrağın gövdesinin bugün derlenmediğini gösterir,
  ama açıldığında derlenip derlenmeyeceğini göstermez — bu yalnızca bayrak
  gerçekten açıldığında bilinir.
- Kapalı kalan bir bayrağın gövdesi zamanla kaynaktaki değişikliklere ayak uyduramaz;
  bunun fark edilmesi bayrağın açıldığı ana kalır.

#### Sonraki Adım

Bu ders, kimsenin bakmadığı bir gövdenin nasıl sessizce durabildiğini gösterdi; bu
sessizliğin nedeni bir hatanın gizlenmesi değil, o gövdenin derleme sürecine hiç
girmemesiydi. Sıradaki ders aynı sorunun tersini soruyor: bir gövde derlemeye
giriyor, tip denetiminden geçiyor ve yine de yanlış çalışabiliyor — bir gövdenin
**doğru çalıştığını** söylemek için derleyicinin denetimi neden yetmiyor ve bu
yetersizliği kapatmak için koşan bir sınamaya ne kadar güvenilebilir? Test ve
Özellik Tabanlı Sınama dersi, derleyicinin hiç sormadığı bu soruyu bu kez kapalı bir
bayrakla değil, koşarak sınanan bir özellikle yanıtlıyor.

### Test ve Özellik Tabanlı Sınama

Source: https://academia.sh/tr/kurslar/rust-ileri/test-ve-ozellik-tabanli-sinama

Sabit tohumlu, belirlenimci bir üreteçle üretilen iki yüz girdide bir özelliğin tutup tutmadığının sayılması ve bu sayının, derleyicinin hiç sormadığı bir soruyu hangi sınırla yanıtladığının gösterilmesi.

Bağımlılık ve Özellik Bayrakları dersi, derleyicinin hiç görmediği bir gövdeyi
ölçtü — orada güvence hiç başlamıyordu. Ama önceki dersin son sorusu farklı bir
yöne işaret ediyordu: `calisan_govde` gibi bir gövde derlemeye giriyor, tip
denetiminden geçiyor, adları doğru çözülüyor ve yine de **yanlış** bir sonuç
üretebiliyor. Derleyici bir gövdenin var olduğunu, tipinin tutarlı olduğunu
denetler; o gövdenin *doğru* olduğunu hiçbir zaman denetlemez — doğruluk tipin
değil, davranışın bir özelliğidir ve tip denetimi davranışı bilmez.

Bu boşluğu kapatmanın bir yolu koşarak sınamaktır: bir gövdeyi belirli girdilerle
çalıştırıp sonucun beklenen bir özelliği taşıyıp taşımadığına bakmak. Bu, tek bir
sabit girdiyle örnek sınaması yazmaktan farklıdır — bir örnek sınaması yalnız o
tek girdide bir şeyin doğru olduğunu gösterir, seçilmemiş hiçbir girdi hakkında
konuşmaz. Bu ders bunu tek bir sabit girdiyle değil, sabit tohumlu bir üreteçle
üretilen çok sayıda girdiyle yapıyor — özellik tabanlı sınamanın temel fikri budur:
tek bir örnek yerine, aynı özelliği birçok üretilmiş örnekte sınamak. Girdilerin belirlenimci bir
üreteçten gelmesi bir yan koşul değil, bu dersin kurduğu gözlemin gerekli
şartlarından biri: sistemin saatine ya da işletim sisteminin rastgelelik
kaynağına dayanan bir üreteç kullanılsaydı, aynı sınama her makinede ve her
koşumda farklı girdilerle çalışırdı ve tutan girdi sayısı koşumdan koşuma değişirdi
— bu ders o değişkenliği baştan dışlıyor. Sınamanın
kendisi denetlenimsiz değildir: hangi özelliğin sınanacağına, girdilerin hangi
aralıktan geleceğine ve kaç girdinin yeteceğine karar veren, derleyici değil,
**sınamayı yazan taraftır**. Kursun ortak sorusu — derleyicinin güvencesi burada
nerede bitiyor — bu derste yanıtını buluyor: tip denetiminin bittiği yerde,
davranış denetimi başlıyor, ama bu denetimin kapsamını da yazan kişi çiziyor.

#### Özelliğin Koşarak Sınanması

Bir özellik tabanlı sınama üç parçadan kurulur: rastgele girdi üreten bir kaynak, bu
girdilerin her birinde sınanacak bir koşul ve koşulun kaç girdide tuttuğunu sayan bir
döngü. Rust'ın standart kütüphanesinde rastgele sayı üreteci yoktur ve bu müfredatta
üçüncü taraf kasa yasaktır; bu yüzden üreteç elle yazılır. **PY5.** Rastgele olması
gereken tek şey girdilerin *dağılımıdır* — sınamanın kendisi belirlenimci kalmalıdır,
aksi hâlde aynı koşum farklı makinelerde farklı sonuç verir. Aşağıdaki üreteç, sabit
bir tohumdan başlayan ve her çağrıda aynı sırayı üreten bir doğrusal uyumlu üreteçtir
(linear congruential generator): tohum aynıysa çıktı dizisi de her koşumda birebir
aynıdır.

Üretecin durumu tek bir `u64` sayıdır ve her çağrıda sabit iki sayıyla çarpılıp
toplanarak güncellenir; `wrapping_mul` ve `wrapping_add` bu işlemde taşmayı sessizce
sarmalar, böylece işlem hiçbir zaman panikle durmaz. `uzunluk` bu ham sayıyı bir
üst sınıra göre daraltır (bir dizinin kaç eleman taşıyacağını belirler), `eleman` ise
`-500` ile `499` arasında bir tam sayı üretir. `vektor` bu ikisini birleştirir: önce
bir uzunluk çeker, sonra o uzunlukta eleman üretir. Kullanılan sabitler bilinen bir
doğrusal uyumlu üreteç ailesinden alınmıştır ve üretecin *iyi dağılması* bu dersin
konusu değildir — burada aranan tek özellik, aynı tohumla her koşumda aynı diziyi
üretmesidir; bu özellik yukarıdaki tüm koşumlarda birebir aynı sayıları verdi.

```rust
// dogrusal_uretec.rs — sabit tohumlu, belirlenimci sozde-rastgele uretec
pub struct DogrusalUretec {
    durum: u64,
}

impl DogrusalUretec {
    pub fn yeni(tohum: u64) -> Self {
        DogrusalUretec { durum: tohum }
    }

    pub fn sonraki(&mut self) -> u64 {
        self.durum = self
            .durum
            .wrapping_mul(6364136223846793005)
            .wrapping_add(1442695040888963407);
        self.durum
    }

    pub fn uzunluk(&mut self, ust_sinir: usize) -> usize {
        (self.sonraki() % (ust_sinir as u64 + 1)) as usize
    }

    pub fn eleman(&mut self) -> i32 {
        (self.sonraki() % 1000) as i32 - 500
    }

    pub fn vektor(&mut self, en_uzun: usize) -> Vec<i32> {
        let n = self.uzunluk(en_uzun);
        (0..n).map(|_| self.eleman()).collect()
    }
}
```

Sınanacak özellik, Proje Yaşam Döngüsü konusunun brifinginde de anılan klasik bir
örnek: bir dizinin ters çevrilmesi iki kez uygulanınca özgün dizi geri gelmeli. Bu
özellik `Vec`in `reverse` yöntemiyle kurulur ve elle üretilen her girdi için sınanır.
İki yüz girdi üretilir, her biri en çok yirmi elemanlı; kaç girdide özelliğin tuttuğu
sayılır.

```rust
// ozellik_sinamasi.rs — ters cevirmenin iki kez uygulanmasi ozdeslik verir mi
mod dogrusal_uretec;
use dogrusal_uretec::DogrusalUretec;

fn ters_ters_ozdeslik(v: &[i32]) -> bool {
    let mut once = v.to_vec();
    once.reverse();
    once.reverse();
    once == v
}

fn main() {
    let mut uretec = DogrusalUretec::yeni(42);
    let toplam_girdi = 200;
    let mut tutan = 0;

    for _ in 0..toplam_girdi {
        let girdi = uretec.vektor(20);
        if ters_ters_ozdeslik(&girdi) {
            tutan += 1;
        }
    }

    println!("sinanan girdi sayisi: {}", toplam_girdi);
    println!("ozelligin tuttugu girdi sayisi: {}", tutan);
}
```

```
sinanan girdi sayisi: 200
ozelligin tuttugu girdi sayisi: 200
```

Üreteç tohum `42` ile başladığında ürettiği ilk dizi on dört elemanlıdır, ikincisi on
bir, üçüncüsü on sekiz — uzunluklar da tıpkı elemanlar gibi sabit tohumla
belirlenimci biçimde değişir. Bu koşumda yazdırılan yalnızca toplam ve tutan
sayılar, ama üretecin ürettiği iki yüz dizinin her biri bu şekilde farklı
uzunluklarda ve farklı elemanlarla geliyor; `vektor`a verilen üst sınır (`20`) bir
dizinin en çok yirmi eleman taşıyabileceğini, en az sıfır (boş dizi) olabileceğini
belirtiyor. En küçük uzunluk sıfır bırakıldı çünkü boş dizi de geçerli bir girdi —
ters çevirmenin iki kez uygulanması boş bir dizide de özdeşlik vermelidir ve üreteç
bu uç durumu da zaman zaman üretiyor.

Ters çevirmenin iki kez uygulanması, özellik tabanlı sınamanın anlatımını
kolaylaştırmak için seçilmiş küçük bir örnek; aynı yaklaşım daha büyük gövdelere de
uygulanabilir. Bir çözümleyicinin (parser) ürettiği bir yapıyı yeniden dizgeye
çevirip tekrar çözümlemek, ilk yapıyla aynı sonucu vermeli; bir sıralama işlevinin
çıktısı, girdiyle aynı elemanları taşımalı ve komşu elemanları sıralı olmalı. Bu
derste seçilen özellik en basitlerinden biri — bir işlemin kendi tersiyle
birleştiğinde özdeşlik vermesi — ama ölçme biçimi (üreteç, özellik, sayaç) aynı
kalıyor: hangi gövde sınanırsa sınansın, derleyicinin denetlemediği yeri koşarak
kapatmak, hep aynı üç parçayı gerektiriyor.

İki yüz girdinin ikisi yüzü de özelliği doğruladı. **PY6.** Bu sayı üreteçle
birlikte belirlenimcidir: tohum `42` olduğu sürece üretilen iki yüz dizinin her biri
aynı sırayla, aynı uzunlukta ve aynı elemanlarla gelir; koşum kaç kez tekrarlanırsa
tekrarlansın sonuç değişmez. Bu, "rastgele" sözcüğünün bu derste ne anlama geldiğini
de netleştiriyor: üretilen girdiler *çeşitlidir* (elle tek tek yazılmamıştır), ama
üretim süreci *rastgele değildir* — her koşumda aynı diziyi üretir.

**Ayrım cümlesi:** M09/K05 kursunun Kapsam Raporlama dersi bir sınamanın **hangi
satırları** çalıştırdığını ölçtü — kapsama oranı, kaynağın ne kadarının koşumdan
geçtiğini gösteriyordu. Bu ders satırları saymıyor; bir özelliğin **kaç girdide
tuttuğunu** sayıyor. Bir satırın çalışması onun doğru çalıştığı anlamına gelmez —
yalnızca çalıştığı anlamına gelir; bu derste ölçülen, çalışmanın doğruluğa dair
verdiği güvencenin gücü.

Güvencenin sınıfı burada **devredildi**: derleyici `ters_ters_ozdeslik` işlevinin
tipini denetledi ve durdu, işlevin *her girdi için* doğru sonuç verip vermediğini
hiç sormadı. Bu soruyu soran ve yanıtlayan, sınamayı yazan taraf oldu — hangi
özelliğin sınanacağına, girdilerin hangi aralıktan geleceğine (`-500`'den `499`'a,
en çok yirmi elemanlı) ve kaç girdinin (iki yüz) yeteceğine yazan karar verdi.
Derleyici bu kararların hiçbirine karışmadı; yalnızca yazılan sınama kodunun
kendisinin tipçe tutarlı olduğunu denetledi.

**Devredildi** sınıfı, `unsafe` Rust dersinde ölçülen devirle aynı sözcüğü taşır ama
farklı bir yerde duruyor: orada devredilen, bir belleğe erişimin sınır içinde
kaldığı sözleşmesiydi; burada devredilen, bir davranışın *doğru* olduğu iddiası.
İkisinde de ortak olan, derleyicinin bir noktada durup geri kalan sorumluluğu
kaynağı yazan tarafa bırakmasıdır. Bu devrin bir sonucu da şu: `ters_ters_ozdeslik`
işlevinin kendisi yanlış yazılmış olsaydı — örneğin ikinci `reverse` çağrısı
unutulsaydı — derleyici bunu da fark etmezdi, çünkü yanlış yazılmış bir sınama da
tıpkı doğru yazılmış biri gibi tip denetiminden geçer. Sınamanın doğruluğu da,
sınadığı gövdenin doğruluğu gibi, yazan tarafın sorumluluğunda kalır.

Girdi sayısının kaç olacağı da aynı devrin bir parçası. Bu derste iki yüz seçildi;
elli seçilseydi sınama daha az girdi görürdü, iki bin seçilseydi daha çok. Sayı büyüdükçe
kapsanan girdi kümesi genişler, ama hiçbir sayı **bütün** girdileri kapsamaz. Derleyici bu sayı hakkında hiçbir görüş bildirmez —
sayı ne kadar küçük olursa olsun ya da ne kadar büyük olursa olsun, sınama kodu
aynı biçimde tip denetiminden geçer ve derlenir. Sayının yeterliliğine karar vermek,
üretecin aralığını seçmeye karar vermekle aynı sorumluluğun parçası: her ikisi de
yazan tarafın elinde, derleyicinin denetim alanının tamamen dışında.

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

Yukarıdaki koşum, seçilen özelliğin **her** girdide tuttuğunu gösterdi — ama bu
sonuç, seçilen özelliğin doğru bir matematiksel gerçek olmasından kaynaklanıyor:
bir diziyi iki kez ters çevirmek, sıralamadan bağımsız olarak her zaman özgün diziyi
verir. Bütün özellikler böyle değildir. Aşağıdaki koşum, bazı girdilerde tutan ama
bazılarında tutmayan bir özelliği aynı üreteçle sınıyor: bir dizinin kendi tersiyle
aynı olması, yani bir bakışımlılık (palindrome) koşulu.

```rust
// sinir_sinamasi.rs — bazi girdilerde tutan, bazilarinda tutmayan bir ozellik
mod dogrusal_uretec;
use dogrusal_uretec::DogrusalUretec;

fn kendi_tersiyle_ayni(v: &[i32]) -> bool {
    let mut ters = v.to_vec();
    ters.reverse();
    ters == v
}

fn main() {
    let mut uretec = DogrusalUretec::yeni(7);
    let toplam_girdi = 200;
    let mut tutan = 0;
    let mut ilk_tutmayan: Option<Vec<i32>> = None;

    for _ in 0..toplam_girdi {
        let girdi = uretec.vektor(6);
        if kendi_tersiyle_ayni(&girdi) {
            tutan += 1;
        } else if ilk_tutmayan.is_none() {
            ilk_tutmayan = Some(girdi);
        }
    }

    println!("sinanan girdi sayisi: {}", toplam_girdi);
    println!("ozelligin tuttugu girdi sayisi: {}", tutan);
    println!("tutmayan ilk girdi uzunlugu: {}", ilk_tutmayan.map(|v| v.len()).unwrap_or(0));
}
```

```
sinanan girdi sayisi: 200
ozelligin tuttugu girdi sayisi: 62
tutmayan ilk girdi uzunlugu: 3
```

**PY7.** İki yüz girdinin altmış ikisinde özellik tuttu, geri kalan yüz otuz
sekizinde tutmadı — üreteç kısa diziler de üretebildiği için (en çok altı eleman,
sıfır dâhil), bazı girdiler tesadüfen bakışımlı çıktı: boş dizi ve tek elemanlı dizi
her zaman kendi tersiyle aynıdır, bu da tutan sayıyı yükseltiyor. Tutmayan ilk
girdinin uzunluğu üç — üç elemanlı bir dizinin bakışımlı olması, elemanların
kendisine bağlı, uzunluğuna değil.

Bu ölçüm, geçen bir özellik sınamasının ne anlama geldiğini sınırlıyor. Önceki
koşumda özellik iki yüz girdinin ikisi yüzünde de tuttu, çünkü sınanan özellik
*her zaman* doğru bir matematiksel gerçekti; bu koşumda özellik yalnız altmış iki
girdide tuttu, çünkü sınanan özellik *bazen* doğru. İkisi arasındaki farkı sınamanın
kendisi göstermiyor — ikisi de "geçen sınama" olarak görünüyor, geçen girdi
sayısının tümüyle eşit olması dışında. Bir özellik sınamasının geçmesi, o özelliğin
sınanan girdi kümesi için tuttuğunu kanıtlar; sınanmayan bir girdi için hiçbir şey
söylemez. İki yüz girdi geniş bir örneklem olabilir, ama üreteç sınırlı bir aralıktan
(`-500` ile `500` arası, en çok yirmi eleman) çekim yapıyor — bu aralığın dışındaki
ya da bu uzunluğun ötesindeki bir girdi hiç sınanmadı. **PY8.** Geçen bir sınamanın
verdiği güvence, sınadığı girdi sayısı ve aralığıyla sınırlıdır; iddia bu sayıya
bağlanmadan yazıldığında güvencenin gerçek kapsamı gizlenmiş olur.

Ölçümün taşıdığı bir sınır daha var: tutmayan ilk girdi yalnız uzunluğuyla
bildirildi, elemanlarıyla değil — bu, ortama bağlı olmayan bir seçim değil, bilinçli
bir seçim. Gerçek özellik tabanlı sınama araçları, tutmayan bir girdi bulunca onu
*küçültme* (shrinking) adı verilen bir süreçle daha küçük, daha okunabilir bir
karşı örneğe indirger; bu ders böyle bir küçültme adımı yazmıyor, yalnız ilk
tutmayan girdinin uzunluğunu bildiriyor. Bu, ölçümün bir başka sınırı: bir özelliğin
tutmadığını göstermek, en *anlaşılır* karşı örneği bulmakla aynı şey değil — bu
derste sayılan yalnız kaç girdide tutup tutmadığı, hangi girdinin en açıklayıcı
karşı örnek olduğu değil.

Altmış iki sayısının kendisi de bir sınır taşıyor: bu, tohum `7` ile üretilen bu
belirli iki yüz girdinin verdiği sayı. Tohum `11` ya da `100` seçilseydi, aynı
özellik aynı aralıkta sınansa bile tutan girdi sayısı farklı çıkardı — çünkü üretilen
girdi kümesi tohuma bağlı olarak değişir. Bu ders bu değişkenliği ölçmüyor; tek bir
tohumla, tek bir koşumu belirlenimci kılıp o koşumun sonucunu bildiriyor.
Belirlenimcilik burada tekrarlanabilirliği garanti ediyor (aynı tohum her zaman
aynı altmış ikiyi verir), ama sınanan aralığın *temsil gücünü* garanti etmiyor —
o güç, aralığın ve üretecin nasıl tasarlandığına bağlı, tek bir koşumun sonucuna
değil.

#### Özet

- Rust'ın standart kütüphanesinde rastgele sayı üreteci yoktur; bu derste sabit
  tohumlu, elle yazılmış bir doğrusal uyumlu üreteç kullanıldı — aynı tohum her
  koşumda aynı girdi dizisini üretir.
- Ters çevirmenin iki kez uygulanmasının özdeşlik verdiği özelliği, iki yüz
  üretilmiş girdinin ikisi yüzünde de tuttu.
- Güvencenin sınıfı **devredildi**: derleyici sınama kodunun tipini denetledi ve
  durdu; hangi özelliğin, hangi girdi aralığında, kaç girdiyle sınanacağına yazan
  taraf karar verdi.
- Bazen doğru olan bir özellik (kendi tersiyle aynı olma), aynı üreteçle sınandığında
  iki yüz girdinin yalnız altmış ikisinde tuttu — geçen bir sınama her girdi için
  doğru anlamına gelmiyor.
- Bu ölçümün sınırı: bir özellik sınamasının verdiği güvence yalnızca sınanan girdi
  sayısı ve aralığı kadardır; aralık dışındaki bir girdi hiç sınanmamıştır.
- Girdi sayısı, aralığı ve sınanan özelliğin kendisi — hangisinin ne kadar
  yeteceği de dâhil — derleyicinin değil, sınamayı yazan tarafın kararıdır; aynı
  üç parça (üreteç, özellik, sayaç) ters çevirme dışındaki gövdelere de uygulanır.

#### Sonraki Adım

Bu ders, bir özelliğin doğru çalıştığını *koşarak* gösterebileceğimizi, ama bu
gösterimin sınırlı sayıda girdiyle ve yazan tarafın kararlarıyla sınırlı kaldığını
ölçtü. Sıradaki ders aynı "koşarak kurulan güvence" fikrini bambaşka bir soruya
uyguluyor: iki yazımdan hangisinin daha az iş yaptığı da yalnız koşularak
öğrenilebilir, doğruluk gibi derleyicinin hiç sormadığı bir soru. Ama bu kez
sınanan doğruluk değil, işlem sayısı; ve girdi sayısı yerine bu kez bir tek girdide
kaç karşılaştırma, kaç kopyalama yapıldığı sayılıyor. Karşılaştırmalı Ölçüm ve
Profilleme dersi, bu sayının neyi *söyleyip* neyi söylemediğini — özellikle neden bir
süre değil — inceliyor.

### Karşılaştırmalı Ölçüm ve Profilleme

Source: https://academia.sh/tr/kurslar/rust-ileri/karsilastirmali-olcum-ve-profilleme

Aynı dizinin iki ayrı sıralama yazımıyla karşılaştırma ve kopyalama sayısının sayılması ve bu sayının bir yazımın süresini öbüründen kısa ilan etmek için neden yetmediğinin gösterilmesi.

Önceki ders bir gövdenin *doğru* çalıştığını koşarak gösterdi, ama bu gösterimin
sınırlı sayıda girdiyle ve sınamayı yazan tarafın kararlarıyla sınırlı kaldığını
ölçtü. Bu ders aynı "koşarak kurulan güvence" fikrini bambaşka bir soruya
uyguluyor: iki yazımdan hangisi daha az iş yapıyor? Derleyici bu soruyu da hiç
sormaz — iki yazım da tip denetiminden geçer, ikisi de derlenir ve derleyici
ikisinin de kaç adımda bir sonuca ulaştığı konusunda hiçbir görüş bildirmez.

Bu ölçümün Rust'a özgü sert bir kısıtı var: derleyicinin kendi dağıtımında bir
karşılaştırmalı ölçüm niteliği bulunur, ama bu nitelik kararlı sürümde açık
değildir — yalnız derleyicinin kararsız (nightly) dalında kullanılabilir. Bu ders
kararlı derleyiciyle çalıştığı için bu niteliği hiç kullanmıyor; süre zaten bu
müfredatın hiçbir
dersinde basılmaz — bir sürenin bu makinede aldığı değer başka bir makinede
geçerli değildir; aynı program aynı makinede bile arka planda koşan başka bir
işten, önbelleğin o an ne tuttuğundan ya da işletim sisteminin zamanlayıcı
kararından etkilenir. Bu yüzden "hangi yazımın süresi kısa" sorusu bu derste hiç
sorulmuyor; onun yerine sorulan, "hangi yazım kaç işlem yapıyor". M09/K05 kursu
Go'da başarım iddialarını süre yerine iş başına ayırma sayısıyla ölçmüştü; bu ders
aynı ilkeyi karşılaştırma ve kopyalama sayısına uyguluyor — ikisi de aynı
gerekçeden geliyor: ortama bağlı olmayan bir sayı, ortama bağlı bir süreden daha
güvenilir bir ölçme birimi.

#### İşlem Sayısıyla Karşılaştırma

Aynı işi yapan iki sıralama yazımı — seçmeli sıralama ve eklemeli sıralama — aynı
dizide koşturuluyor. İkisi de sonucu doğru sıralar, ama farklı sayıda karşılaştırma
ve farklı sayıda kopyalama yaparak sıralar. Bu farkı görünür kılmak için iki yazım
da paylaşılan bir sayaca her karşılaştırmada ve her kopyalamada bir artış
yazıyor.

Seçmeli sıralama, dizinin sıralanmamış kalan kısmında en küçük elemanı arar, onu
bulunca sıralı kısmın hemen ardına taşır ve bu adımı dizinin sonuna kadar tekrarlar
— her adımda kalan bütün elemanlara bakılır, ama yer değiştirme yalnızca bir kez
olur. Eklemeli sıralama ise dizinin başından başlar, her yeni elemanı zaten sıralı
duran önceki kısmın doğru yerine kadar geri kaydırarak yerleştirir — bir eleman
yerine oturana kadar birden çok kaydırma gerekebilir, ama her kaydırma yalnızca
komşu elemanlarla karşılaştırma yapar. İki yazım da aynı sonuca varır; ölçülen,
oraya varmak için attıkları adım sayısı.

```rust
// islem_sayaci.rs — kars ve kopya sayaci, iki siralama yazimi arasinda paylasilir
#[derive(Default)]
pub struct IslemSayaci {
    pub karsilastirma: u64,
    pub kopyalama: u64,
}

pub fn secmeli_sirala(dizi: &mut [i32], sayac: &mut IslemSayaci) {
    let n = dizi.len();
    for i in 0..n {
        let mut en_kucuk = i;
        for j in (i + 1)..n {
            sayac.karsilastirma += 1;
            if dizi[j] < dizi[en_kucuk] {
                en_kucuk = j;
            }
        }
        if en_kucuk != i {
            dizi.swap(i, en_kucuk);
            sayac.kopyalama += 1;
        }
    }
}

pub fn eklemeli_sirala(dizi: &mut [i32], sayac: &mut IslemSayaci) {
    let n = dizi.len();
    for i in 1..n {
        let deger = dizi[i];
        let mut j = i;
        while j > 0 {
            sayac.karsilastirma += 1;
            if dizi[j - 1] <= deger {
                break;
            }
            dizi[j] = dizi[j - 1];
            sayac.kopyalama += 1;
            j -= 1;
        }
        dizi[j] = deger;
        sayac.kopyalama += 1;
    }
}
```

**PY9.** `secmeli_sirala` her adımda kalan en küçük elemanı arıyor ve onu bulunca
tek bir takasla (`swap`) yerine koyuyor — bu yüzden karşılaştırma sayısı çok, ama
kopyalama sayısı az. `eklemeli_sirala` ise her elemanı zaten sıralı kısmın doğru
yerine kaydırarak yerleştiriyor — karşılaştırma sayısı girdinin düzenine bağlı,
kopyalama sayısı kaydırılan eleman sayısına bağlı. İki yazım da aynı sonucu üretiyor,
ama farklı bir yoldan.

```rust
// olcum.rs — ayni dizi uzerinde iki siralama yaziminin islem sayisi
mod islem_sayaci;
use islem_sayaci::{eklemeli_sirala, secmeli_sirala, IslemSayaci};

fn main() {
    let girdi: [i32; 12] = [9, 3, 7, 1, 8, 2, 6, 4, 10, 5, 12, 11];

    let mut dizi_a = girdi;
    let mut sayac_a = IslemSayaci::default();
    secmeli_sirala(&mut dizi_a, &mut sayac_a);

    let mut dizi_b = girdi;
    let mut sayac_b = IslemSayaci::default();
    eklemeli_sirala(&mut dizi_b, &mut sayac_b);

    println!("secmeli sonuc:  {:?}", dizi_a);
    println!("eklemeli sonuc: {:?}", dizi_b);
    println!("ikisi de ayni sonucu verdi: {}", dizi_a == dizi_b);
    println!();
    println!("{:<12}{:<14}{}", "yazim", "karsilastirma", "kopyalama");
    println!("{:<12}{:<14}{}", "secmeli", sayac_a.karsilastirma, sayac_a.kopyalama);
    println!("{:<12}{:<14}{}", "eklemeli", sayac_b.karsilastirma, sayac_b.kopyalama);
}
```

```
secmeli sonuc:  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
eklemeli sonuc: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
ikisi de ayni sonucu verdi: true

yazim       karsilastirma kopyalama
secmeli     66            9
eklemeli    32            34
```

On iki elemanlı bu dizide seçmeli sıralama altmış altı karşılaştırma ve dokuz
kopyalama yaptı; eklemeli sıralama otuz iki karşılaştırma ve otuz dört kopyalama
yaptı. **PY10.** Hiçbir yazım her iki sütunda da "daha az" değil: seçmeli sıralama
karşılaştırmada kaybediyor ama kopyalamada kazanıyor, eklemeli sıralama tam tersi.
Tek bir sayı ("toplam işlem") üretmek bu farkı gizlerdi; iki ayrı sayaç tutmak,
hangi yazımın hangi kaynağı daha çok kullandığını ayrı ayrı görünür kılıyor.

Karşılaştırma ve kopyalamayı tek bir toplamda birleştirmenin nasıl yanıltıcı
olabileceği bu sayılarda görülebilir: yetmiş beş (`66 + 9`) ile altmış altı
(`32 + 34`) toplandığında eklemeli sıralama daha "az işlem yapmış" gibi görünür. Ama
bu toplam, bir karşılaştırmayla bir kopyalamayı aynı birimden sayıyor — oysa ikisi
farklı işlemler ve farklı maliyetler taşıyabilir. Sayaç bu iki işlemi bilerek ayrı
tuttu; birleştirilmiş bir toplam, seçmeli sıralamanın kopyalamada ne kadar
tutumlu olduğunu (yalnızca dokuz) gizler.

Bu iki sayaç aynı zamanda bir profilleme biçimi. Karşılaştırmalı ölçüm iki yazımı
birbiriyle kıyaslarken, profilleme tek bir yazımın **nereye** iş harcadığını
sorar — hangi adım, hangi işlem türü toplam işin ne kadarını oluşturuyor.
`IslemSayaci`nin iki ayrı alanı tam olarak bunu yapıyor: seçmeli sıralamanın işinin
neredeyse tamamı karşılaştırmada (altmış altı karşılaştırmaya karşı dokuz
kopyalama), eklemeli sıralamanın işi ise iki tür arasında daha dengeli dağılıyor
(otuz iki karşılaştırmaya karşı otuz dört kopyalama). Bir profilleme aracı gerçek
bir programda genellikle hangi işlevin toplam sürenin ne kadarını tükettiğini
gösterir; bu ders süre yerine işlem türünü kullanıyor, ama soru aynı: iş nereye
gidiyor?

**Ayrım cümlesi:** M09/K05 kursunun Karşılaştırmalı Ölçüm dersi bir gövdenin iş
başına kaç ayırma yaptığını ölçtü — orada sayılan, öbek üzerinde yeni bellek ayrılıp
ayrılmadığıydı ve bir işlev çağrısı içinde kaç kez tekrarlandığıydı. Bu derste hiç
bellek ayırma yok; her iki sıralama da girdi dizisini yerinde (`&mut [i32]`) sıralıyor,
yeni bir dizi ya da yapı kurmuyor. Sayılan, karşılaştırma ve kopyalama gibi doğrudan
algoritmanın adımları. İkisi de süre yerine bir sayı kullanıyor, ama sayının neyi
temsil ettiği farklı: biri kaynak tüketimini (kaç kez öbekten yer istendiğini), bu
ders algoritmanın kendi iş adımlarını (kaç kez iki değer karşılaştırıldığını, kaç
kez bir değer taşındığını) sayıyor.

Bu sayının verdiği güvence **koşarak** geliyor, derleyiciden değil — ve kimse bu
sayıyı sürekli izlemiyor. Derleyici iki yazımın da tipini denetledi, ikisinin de aynı
imzayı taşıdığını doğruladı, ama hangisinin daha az karşılaştırma yaptığı sorusunu
hiç sormadı; bu soruyu yalnız yukarıdaki koşum yanıtladı ve yanıt yalnız bu koşum
için geçerli — dizi değişirse, birinin kaynak kodu değişirse, aynı ölçümü yeniden
koşturmadan sayıların hâlâ geçerli olduğunu söylemenin bir yolu yok. Bu derste
ölçülen sınıf **denetlenmiyor**: iki yazımın göreli maliyeti hiçbir araç tarafından
sürekli gözetilmiyor, yalnızca istendiğinde tek seferlik bir koşumla öğrenilebiliyor.

Bu, önceki derste ölçülen **devredildi** sınıfından ayrı bir yer. Orada bir özellik
sınaması vardı ve o sınama, yeniden koşturulduğunda aynı soruyu yeniden yanıtlıyordu
— sorumluluk yazan tarafa devredilmişti ama bir sınama biçiminde sürekli hâle
getirilebilirdi. Bu derste öyle bir sınama yok; yukarıdaki koşum bir kerelik bir
gözlem ve onu bir sınamaya dönüştürüp kaynak değiştikçe otomatik yeniden
çalıştırmak bu dersin kapsamı dışında. Böyle bir sınama yazılsaydı bile, neyin
"iyi" sayılacağına (hangi sayının hangi sınırın altında kalması gerektiğine) yine
yazan taraf karar verirdi — ama en azından o zaman denetim sürekli hâle gelirdi.
Bugünkü hâliyle denetim yalnız bu dersin koşumuyla sınırlı.

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

Yukarıdaki sayılar tek bir girdiye, on iki elemanlı karışık bir diziye ait. Aynı iki
yazım farklı bir girdide koşturulunca sayılar nasıl değişiyor? Aşağıdaki koşum aynı
iki işlevi, zaten sıralı bir diziye uyguluyor.

```rust
// sinir_olcum.rs — ayni yazimlar, siradan bir dizide islem sayisi nasil degisir
mod islem_sayaci;
use islem_sayaci::{eklemeli_sirala, secmeli_sirala, IslemSayaci};

fn main() {
    let siradan: Vec<i32> = (1..=12).collect();

    let mut a = siradan.clone();
    let mut sayac_a = IslemSayaci::default();
    secmeli_sirala(&mut a, &mut sayac_a);

    let mut b = siradan.clone();
    let mut sayac_b = IslemSayaci::default();
    eklemeli_sirala(&mut b, &mut sayac_b);

    println!(
        "secmeli kars={} kopya={}",
        sayac_a.karsilastirma, sayac_a.kopyalama
    );
    println!(
        "eklemeli kars={} kopya={}",
        sayac_b.karsilastirma, sayac_b.kopyalama
    );
}
```

```
secmeli kars=66 kopya=0
eklemeli kars=11 kopya=11
```

**PY11.** Seçmeli sıralamanın karşılaştırma sayısı değişmedi: hâlâ altmış altı —
girdi zaten sıralı olsa da seçmeli sıralama kalan her elemanı yine de tek tek
tarıyor, çünkü en küçüğü bulmanın tek yolu hepsine bakmak. Eklemeli sıralamanın
karşılaştırma sayısı ise otuz ikiden on bire düştü ve kopyalama sayısı otuz dörtten
on bire düştü: zaten sıralı bir dizide her eleman bir önceki elemandan büyük
olduğu için tek bir karşılaştırmayla yerini koruyor, hiç kaydırma gerekmiyor.

Tersi yönde bir girdi tam tersi bir sonuç verir. Dizi büyükten küçüğe sıralanmış
olsaydı, eklemeli sıralamanın her elemanı sıralı kısmın en başına kadar kaydırması
gerekirdi — kopyalama sayısı karışık dizideki otuz dörtten de yukarı çıkar, seçmeli
sıralamanınki yine altmış altıda sabit kalırdı. Aynı iki yazım, üç farklı girdi
düzeninde (karışık, sıralı, ters sıralı) birbirine göre üç farklı konumda durur:
bazen biri az iş yapar, bazen öbürü, bazen ikisi de aynı sabit sayıda. Seçmeli
sıralamanın karşılaştırma sayısının hep aynı kalması bir kusur değil, algoritmanın
yapısının doğrudan sonucu — hangi eleman nerede olursa olsun, kalan her eleman yine
de taranır.

Bu, ilk koşumun sonucunu neredeyse tersine çeviriyor, tam tersine değil: seçmeli
sıralamanın karşılaştırma sayısı hâlâ eklemeli sıralamanınkinden büyük, ama aradaki
oran çok değişti. İlk dizide eklemeli sıralama daha az
karşılaştırma yapmıştı; bu dizide de öyle, ama fark çok büyüdü — otuz ikiye
karşı altmış altıdan, on bire karşı altmış altıya. Aynı iki yazım, aynı uzunluktaki
farklı bir girdide, birbirine göre çok farklı bir oranda duruyor. **PY12.** Bu, işlem
sayısının **girdiye bağlı** olduğunu ve tek bir girdide alınan bir ölçümün başka bir
girdi için hiçbir şey söylemediğini gösteriyor — seçmeli sıralamanın karşılaştırma
sayısı girdiden bağımsızken, eklemeli sıralamanınki girdiye tamamen bağlı.

İşlem sayısının kendisi de süre demek değil. Bir karşılaştırma ile bir kopyalama
aynı maliyeti taşımayabilir — kopyalanan değer büyük bir yapıysa bir kopyalama, bir
tam sayı karşılaştırmasından çok daha pahalı olabilir; burada `i32` kopyalandığı için
bu fark küçük, ama sayaç bu farkı hiç bilmiyor, yalnız kaç kez çalıştıklarını
sayıyor. Daha az işlem yapan bir yazımın süresinin gerçekte daha uzun olması da
mümkün: işlemlerin türü, bellek erişim düzeni ve donanımın kendisi sonucu etkiler.

Bellek erişim düzeni bunun somut bir örneği. Seçmeli sıralama kalan elemanları
tararken diziyi sürekli baştan sona okur; eklemeli sıralama ise yalnız yakın
komşularla ilgilenir. Bu iki erişim örüntüsü donanımın önbelleğini farklı
kullanabilir ve bu fark işlem sayısında hiç görünmez — iki yazım aynı sayıda
karşılaştırma yapsa bile, biri önbelleğe daha uygun bir sırayla erişebilir, öbürü
daha dağınık bir sırayla. Bu ders böyle bir farkı ölçmüyor, çünkü ölçmek gerçek
donanımı ve gerçek süreyi devreye sokar; burada yalnız "kaç kez" sorusu
yanıtlanıyor, "hangi sürede" değil. Bu yüzden yukarıdaki tablo "eklemeli sıralama
daha iyi" diye okunmamalı — yalnızca "bu girdide eklemeli sıralama daha az
karşılaştırma ve daha az kopyalama yaptı" diye okunmalı, ki bu bile bir sonraki
girdide tekrarlanacağının garantisi değil.

#### Özet

- İki sıralama yazımı (`secmeli_sirala`, `eklemeli_sirala`) paylaşılan bir sayaçla
  karşılaştırma ve kopyalama sayısını ayrı ayrı sayarak karşılaştırıldı; süre hiçbir
  yerde ölçülmedi, basılmadı ya da karşılaştırılmadı.
- Karışık bir on iki elemanlı dizide seçmeli sıralama altmış altı karşılaştırma dokuz
  kopyalama, eklemeli sıralama otuz iki karşılaştırma otuz dört kopyalama yaptı.
- Güvencenin sınıfı **denetlenmiyor**: hangi yazımın kaç işlem yaptığı hiçbir araç
  tarafından sürekli gözetilmiyor, yalnız istenildiğinde tek seferlik bir koşumla
  öğrenilebiliyor.
- Zaten sıralı bir dizide seçmeli sıralamanın karşılaştırma sayısı değişmedi
  (altmış altı), eklemeli sıralamanınki on bire düştü — işlem sayısı girdiye bağlı.
- İşlem sayısı süre demek değildir: bir karşılaştırma ile bir kopyalama aynı maliyeti
  taşımaz ve daha az işlem yapan bir yazımın süresi gerçekte daha uzun da olabilir.
- Tek bir girdide alınan bir ölçüm, başka bir girdi için hiçbir şey söylemez; "daha
  az işlem" iddiası hangi girdide ölçüldüğüne bağlanmadan yazılamaz.

#### Sonraki Adım

Bu ders, iki yazımın göreli maliyetinin yalnızca koşularak öğrenilebildiğini, bu
öğrenmenin tek bir girdiyle sınırlı kaldığını ve derleyicinin bu konuda hiçbir
görüş bildirmediğini gösterdi — üç ders de (Bağımlılık ve Özellik Bayrakları'ndaki
kapalı bayrak, Test ve Özellik Tabanlı Sınama'daki koşarak kurulan güvence, bu
dersteki işlem sayısı) aynı temel gerçeğin farklı yüzleri: derleyicinin durduğu
yerde, kalan sorumluluk ya hiç kimseye kalıyor ya da koşan bir sürece devrediliyor.
Sıradaki ve bu konunun son dersi, aynı "derleyicinin hiç bakmadığı yer" sorusunu
bambaşka bir kaynağa uyguluyor: bir kod bloğunun yanına yazılan açıklama.
Belgelendirme dersi, bir belge cümlesinin doğru olup olmadığını hiçbir aracın
denetlemediğini, ama aynı belgenin içine gömülü çalışan bir örneğin gerçekten
sınanabildiğini — aynı kaynakta iki farklı güvence sınıfının yan yana
durabildiğini — gösteriyor.

### Belgelendirme

Source: https://academia.sh/tr/kurslar/rust-ileri/belgelendirme

Bir belge cümlesinin işlevin gerçek davranışıyla çeliştiği ama hiçbir araç tarafından denetlenmediği ve aynı işlevin belge içindeki çalışan örneğinin, belge aracı program içinden çağrılarak gerçekten sınandığı gösterilerek, aynı kaynakta iki güvence sınıfının nasıl yan yana durabildiğinin ölçülmesi.

Önceki üç ders, kaynakta duran ama derleyicinin hiç bakmadığı üç ayrı yeri ölçtü:
kapalı bir bayrağın gövdesi, koşarak sınanan bir özellik, iki yazımın işlem sayısı.
Üçünde de ölçülen şey kaynağın *kod* olan bir parçasıydı — bir işlev, bir blok, bir
döngü. Bu konunun (Proje Yaşam Döngüsü) dördüncü ve son dersi, aynı soruyu kaynağın en
sık göz ardı edilen bölümüne soruyor: bir işlevin yanına yazılan açıklama. Bir belge
cümlesi de kaynağın bir parçası, ama derleyicinin denetlediği bir sözdizimi değil,
insanın okuduğu bir düzyazı; bu ders, bu düzyazının doğruluğunu kimin (ya da hiç
kimsenin) denetlediğini soruyor — ve aynı kaynakta iki farklı yanıtın yan yana
durabildiğini gösteriyor.

Bu sorunun cevabı tek bir cümleyle verilemiyor, çünkü bir belge yorumunun iki
farklı bölümü var: yukarıda okunan düzyazı ve genellikle onun altına yerleştirilen,
işlevin nasıl çağrılacağını gösteren küçük bir kod örneği. Bu iki bölüm aynı
yorumun içinde birlikte duruyor, ama kaderleri aynı değil — bu dersin ölçtüğü tam
olarak bu ayrım. Bu konunun diğer üç dersi kendi ölçümlerini kurdu; bu ders,
kursun on iki gözlemlik güvence çekirdeğine geri dönüp onun "belge metninin
dogrulugu" satırını aynen kullanıyor.

#### Belgenin Kendisi ve Kaynaktaki Çelişki

Rust'ta bir işlevin üstüne üç yatık çizgiyle (`///`) başlayan satırlar yazmak, o
işlevin belge yorumunu oluşturur. Bu yorum sıradan bir açıklamadan farklı: içine
`# Ornekler` gibi alt başlıklar ve ters tırnaklarla çevrelenmiş kod parçaları
konabilir. Aşağıdaki üç işlevin üçünde de bu düzen var — bir açıklama cümlesi ve
altında bir örnek. İlk işlev, güvence çekirdeğinin (`guvence.rs`) "belge metninin
dogrulugu" kesitidir: belge satırı işlevin **en büyük** değeri döndürdüğünü
söylüyor, işlev gerçekte **en küçüğü** döndürüyor. Diğer iki işlev — küçük olanı
döndüren ve bir dilimi toplayan — belgeleri doğru yazılmış, karşılaştırma için
buraya eklendi.

```rust
// belge_govdesi.rs — guvence.rs'in "belge metninin dogrulugu" kesiti + iki sinanan ornek daha
/// Bu belge satiri islevin **en buyuk** degeri dondurdugunu soyluyor.
/// Islev en kucugu donduruyor. Hicbir arac bu celiskiyi denetlemiyor.
///
/// # Ornekler
///
/// ```
/// assert_eq!(belge_govdesi::belgesi_yanlis(3, 9), 3);
/// ```
pub fn belgesi_yanlis(a: i32, b: i32) -> i32 {
    if a < b {
        a
    } else {
        b
    }
}

/// Iki degerden kucuk olanini dondurur.
///
/// # Ornekler
///
/// ```
/// assert_eq!(belge_govdesi::kucuk_olan(3, 9), 3);
/// ```
pub fn kucuk_olan(a: i32, b: i32) -> i32 {
    if a < b {
        a
    } else {
        b
    }
}

/// Bir dilimin toplamini dondurur.
///
/// # Ornekler
///
/// ```
/// assert_eq!(belge_govdesi::topla(&[1, 2, 3]), 6);
/// ```
pub fn topla(dilim: &[i32]) -> i32 {
    dilim.iter().sum()
}
```

**PY13.** Bu dosya hiç `fn main` taşımıyor; bir kaynak kesiti olarak duruyor. Üç
işlevin üçü de doğru derleniyor — `belgesi_yanlis`in prose'u yanlış olsa da gövdesi
tipçe tutarlı ve derleyici prose'u hiç okumuyor. Aşağıdaki gövde bu üç işlevi
çağırıyor.

```rust
// belge_govdesi_main.rs — belge celiskisi kaynakta duruyor, program yine de derleniyor
mod belge_govdesi;
use belge_govdesi::{belgesi_yanlis, kucuk_olan, topla};

fn main() {
    println!("belge 'en buyuk' diyor, gercek sonuc: {}", belgesi_yanlis(3, 9));
    println!("kucuk_olan(3, 9) = {}", kucuk_olan(3, 9));
    println!("topla(&[1, 2, 3]) = {}", topla(&[1, 2, 3]));
}
```

```
belge 'en buyuk' diyor, gercek sonuc: 3
kucuk_olan(3, 9) = 3
topla(&[1, 2, 3]) = 6
```

Derleme hiçbir uyarı basmadı. `belgesi_yanlis(3, 9)` çağrısı `3`ü döndürdü — küçük
olanı, belge cümlesinin söylediği gibi büyük olanı değil. Bu, sıradan bir derleme
sürecinde beklenen davranış: `rustc`, üç yatık çizgiyle (`///`) başlayan bir belge
yorumunu Markdown olarak ayrıştırmaz, içindeki cümleleri okumaz, içine gömülü kod
örneğini derlemez. Belge yorumunun tamamı — hem yanlış prose hem de doğru
çalışan örnek — sıradan bir derlemede **görünmez metin**dir; derleyici onu ne
doğrular ne de reddeder, çünkü ona hiç bakmaz.

Bu, güvence çekirdeğinin ölçtüğü sınıfın birebir kendisi: **denetlenmiyor**.
Derleyici işlevin *gövdesini* denetledi (tipini kontrol etti, adları çözdü) ama
işlevin *belgesini* hiç görmedi. Bir belge cümlesi kaynağın parçası olsa da,
derleyicinin ilgi alanının tamamen dışında duruyor — tıpkı Bağımlılık ve Özellik
Bayrakları dersindeki kapalı bayrağın gövdesi gibi, ama farklı bir nedenle: orada
gövde derlemeye hiç girmiyordu, burada girmiyor değil, derleyici onu **başka bir
kategoriden** görüyor — kod değil, düzyazı.

Bu iki durumu birbirinden ayırmak önemli. Kapalı bir bayrağın gövdesi, bayrak
açıldığında derlemeye girebilecek bir kod parçasıdır — potansiyel olarak
denetlenebilir, yalnız bugün denetlenmiyor. Bir belge cümlesinin prose kısmı ise
hiçbir koşulda derlemeye girmez; o hiçbir zaman kod olmadı, olmayacak da. Derleyici bir `///` belge yorumunu kaynağın diğer bölümlerinden ayırıp işlevin
üstüne iliştirilmiş bir bilgi olarak saklar — bu yüzden tamamen yok sayılmıyor,
bir belge aracının okuyabileceği bir yerde duruyor. Ama sıradan derlemenin tip
denetimi ve ad çözümlemesi aşamaları bu bilginin içeriğine hiç bakmaz: içindeki
cümleyi doğrulamaz, içindeki kod örneğini derlemez. Denetlenen ile saklanan farklı
şeyler.

Bu, Bağımlılık ve Özellik Bayrakları dersinde ölçülen çürümeyle aynı kökten geliyor.
Orada kapalı bir bayrağın gövdesi, kaynağın geri kalanı değiştikçe uyum
yitirebiliyordu, çünkü hiç kimse onu denetlemiyordu. Burada aynı şey bir işlevin
gövdesiyle belgesi arasında oluyor: gövde zamanla değişebilir — bir hata
düzeltilir, bir davranış tersine çevrilir — ama üstündeki belge cümlesi kendiliğinden
güncellenmez. `belgesi_yanlis` muhtemelen bir zamanlar doğru yazılmıştı (belki
işlev gerçekten en büyüğü döndürüyordu) ve gövde değiştiğinde belge cümlesi
unutuldu; bunu kanıtlamanın bir yolu yok, ama iki tarafın da bağımsız yaşayabildiği
gerçeği, böyle bir sürüklenmenin neden mümkün olduğunu açıklıyor. İkisinde de ortak
olan, hiçbir denetim mekanizmasının iki tarafı birbirine bağlı tutmaması.

#### Belge İçindeki Örnek: Program İçinden Sınama

Yukarıdaki her işlevin belgesinde bir `# Ornekler` bölümü ve içinde üç ters
tırnaklı bir kod örneği var. Bu örnekler sıradan derlemede görünmez olsa da, dilin
kendi belge aracı bunları özel olarak ele alır: kaynağı tarayıp her örneği ayrı bir
sınama gibi derler ve çalıştırır. Bu, bu müfredatta program içinden kurulan ikinci
ölçüm biçimi — Prosedürel Makrolar dersinde derleyicinin kendisi program içinden
çağrılmıştı, burada çağrılan derleyici değil, derleyicinin bir yan aracı.
Aşağıdaki program bu aracı `std::process::Command`
ile kendi içinden çağırıyor ve yalnız kaç örneğin geçtiğini basıyor — yol, süre ve
makine adı basılmıyor.

```rust
// belge_sinamasi.rs — belge aracini program icinden cagirip gecen sinama sayisini basar
use std::process::Command;

fn main() {
    let derleme = Command::new("rustc")
        .args([
            "--edition", "2021", "--crate-type", "lib",
            "-o", "libbelge_govdesi.rlib", "belge_govdesi.rs",
        ])
        .output()
        .expect("derleyici baslatilamadi");
    if !derleme.status.success() {
        println!("gecen sinama sayisi: 0");
        return;
    }

    let sinama = Command::new("rustdoc")
        .args(["--edition", "2021", "--test", "-L", ".", "belge_govdesi.rs"])
        .output()
        .expect("belge araci baslatilamadi");

    let cikti = String::from_utf8_lossy(&sinama.stdout);
    let sayi = cikti
        .lines()
        .find(|s| s.starts_with("test result:"))
        .and_then(|satir| satir.split("passed").next())
        .and_then(|parca| parca.trim().rsplit(' ').next())
        .and_then(|s| s.parse::<u32>().ok())
        .unwrap_or(0);

    println!("gecen sinama sayisi: {}", sayi);
}
```

```
gecen sinama sayisi: 3
```

**PY14.** Üç örneğin üçü de geçti — `belgesi_yanlis`, `kucuk_olan` ve `topla`nın
örnekleri. Bu program iki adımda çalışıyor. İlk adımda kaynağı bir kütüphane
olarak derliyor; bu adım gerekli çünkü her örnek kaynaktaki gerçek işlevleri
`belge_govdesi::belgesi_yanlis` gibi tam adlarla çağırıyor ve belge aracının bu
adları çözebilmesi için önceden derlenmiş kütüphaneyi bulması gerekiyor. İkinci
adımda belge aracı sınama kipinde çağrılıyor; `-L .` seçeneği aracın önceden
derlenmiş kütüphaneyi çalışma dizininde arayacağını belirtiyor. Aracın kendi çıktısı
satır satır bir yol, bir geçen süre ve ayrıntılı bir döküm içerir; bu program o
çıktının içinden yalnız "geçti" satırındaki tek bir sayıyı çıkarıyor ve geri
kalanını hiç yazdırmıyor — ortama bağlı hiçbir alan bu programın kendi çıktısına
karışmıyor.

Bu koşum, önceki bölümdeki gözlemi tersine çeviriyor. Sıradan derlemede belge
tamamen görünmezdi; burada belge aracı aynı kaynağı **farklı bir gözle** okuyor —
her `# Ornekler` bloğundaki kod parçasını ayrı bir kaynak dosyası gibi ele alıp
gerçekten derliyor ve koşturuyor. `belgesi_yanlis`in örneği içindeki eşitlik
sınayan çağrıyla gerçek davranışı (`3` sonucunu) sınıyor ve **geçiyor** — çünkü
örnek kodun kendisi doğru yazılmış, yanlış olan yalnız üstündeki prose cümlesi. Bu derste ölçülen
sınıf burada değişiyor: **sürdürüyor**. Belge aracı, dilin kendi dağıtımının bir
parçası ve tıpkı `rustc`nin bir gövdeyi denetlediği gibi, örneği derleyip
çalıştırarak denetliyor.

Bu, önceki bölümde anlatılan çürümeye karşı bir savunma sağlıyor — ama yalnızca
örnek için, prose için değil. `kucuk_olan`ın imzası değişseydi (örneğin iki yerine
üç argüman alsaydı), örneğindeki çağrı artık derlenmezdi ve belge aracı bunu
"başarısız" olarak işaretlerdi; örnek, gövdeyle senkron kalmaya **zorlanıyor**.
`belgesi_yanlis`in prose'unu ise hiçbir mekanizma gövdeyle senkron tutmuyor —
gövde değişse de değişmese de, cümle olduğu gibi kalır. Belge aracının verdiği
güvence bu yüzden gerçek ama dar: yalnız örnek gövdeyle senkron kalmaya zorlanıyor,
onu çevreleyen açıklama değil.

**Ayrım cümlesi:** M09/K05 kursunun ölçü ekseni bir bilginin **kim tarafından**
söylendiğiydi — kaynak mı, araç mı, koşum mu. Bir belge cümlesi tam olarak
**kaynağın söylediği** bir bilgidir; bu ders o bilginin doğrulanıp
doğrulanmadığını soruyor. Yanıt ikiye ayrılıyor: düzyazı kısmı hiçbir araç
tarafından okunmuyor (denetlenmiyor), örnek kısmı ise dilin kendi belge aracı
tarafından koşturuluyor (sürdürüyor) — aynı kaynak cümlesinin iki farklı parçası,
iki farklı akıbete uğruyor. M09/K05'in üçlü ayrımıyla karşılaştırıldığında, bir
belge cümlesinin prose'u tam da "kaynağın söylediği" kategoriye düşer — hiçbir
araç ya da koşum onu doğrulamaz; belge cümlesinin örneği ise bu üçlünün dışına
çıkar, çünkü artık kaynağın söylediği bir şey olmaktan çıkıp **koşumun
doğruladığı** bir şeye dönüşür.

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

Yukarıdaki iki koşum bir arada okunduğunda çarpıcı bir sonuç ortaya çıkıyor:
`belgesi_yanlis`in örneği geçen üç sınamadan biri ve onun **geçmesi**, üstündeki
prose cümlesinin hâlâ yanlış olduğu gerçeğini hiç değiştirmiyor. Örnek `3`ü
sınıyor ve `3` doğru sonuç; prose "en büyük" diyor ve bu yanlış. Belge aracı
örneği çalıştırıp doğru sonucu aldığı için sınamayı **geçti** olarak işaretliyor
— prose'un ne söylediğine hiç bakmadan.

**PY15.** Bu, bu ölçümün en önemli sınırı: belge aracının denetlediği şey
**örnektir, açıklama değil.** Bir örnek, içindeki iddiaları (eşitlik sınayan
çağrıyla yazılan beklenen değerleri) sınar; örneğin üstündeki düzyazı cümlesi
neyi anlattığını hiçbir biçimde etkilemez ya da denetlemez. Bir kişi `belgesi_yanlis`in
belgesini okuyup "en büyüğü döndürüyor" diye anlayabilir, aynı belgenin altındaki
örneği görüp onun geçtiğini de bilebilir — ve yine de yanlış bir izlenimle kaynaktan
ayrılabilir, çünkü geçen örnek prose'un doğruluğuna dair hiçbir şey söylemiyor.

Bu sınırın kapsamı da belirgin: belge aracı yalnız ` ``` ` ile işaretlenmiş kod
bloklarını görür; bu blokların dışındaki her cümle, `# Ornekler` başlığı da dâhil,
onun için sıradan metindir. Bir örneğin doğru yazılmış olması, o örneğin
açıklamayla **tutarlı** olduğu anlamına gelmez — yalnızca örneğin kendi içinde
doğru (kaynak koddaki gerçek davranışla eşleştiği) anlamına gelir. Bu derste
`kucuk_olan` işlevi tam olarak `belgesi_yanlis` ile aynı gövdeye sahip, ama
prose'u doğru; ikisinin de örneği aynı sınamadan geçiyor, çünkü örnek yalnız
kod düzeyinde neyin çağrıldığını ve neyin döndüğünü sınıyor, iki işlevin prose'unu
karşılaştırmıyor.

Bu sınırın bir sonucu daha var: bir örneğin geçmesi, o örneğin **temsil ettiği**
durumun eksiksiz olduğu anlamına da gelmez. `belgesi_yanlis`in örneği yalnız tek
bir çağrıyı (`3` ile `9`) sınıyor; işlev başka bir girdiyle çağrıldığında ne
döndüğü bu örnekte hiç görünmüyor. Belge aracı bu örneği geçti olarak işaretlerken,
işlevin *bütün* girdiler için ne yaptığına dair hiçbir şey söylemiyor — tıpkı Test
ve Özellik Tabanlı Sınama dersinde görülen sınırın burada da geçerli olması gibi:
sınanan girdi kadar güvence, ne fazlası ne eksiği. Belge aracının verdiği güvence bu
yüzden iki kat sınırlı: yalnız örneği sınar (prose'u değil) ve örneğin sınadığı
girdiyi sınar (işlevin bütün davranışını değil).

#### Özet

- Sıradan bir derlemede (`rustc`) bir belge yorumunun hem prose'u hem de içindeki
  kod örneği görünmezdir; derleyici ikisine de bakmaz.
- `belgesi_yanlis` işlevinin belge cümlesi "en büyük" diyor, gövde "en küçüğü"
  döndürüyor; bu çelişki sıradan derlemede uyarısız geçiyor — sınıf **denetlenmiyor**.
- Belge aracı program içinden çağrılıp yalnız geçen sınama sayısı (`3`) basıldı;
  süre, yol ve makine adı hiç yazdırılmadı.
- Belge aracı `# Ornekler` içindeki kodu gerçekten derleyip çalıştırır; bu bölüm
  için sınıf **sürdürüyor** — dilin kendi aracı, `rustc`nin denetimini bir adım
  daha ileri taşıyor.
- Sınırlayıcı ölçüm: `belgesi_yanlis`in örneği geçti, ama üstündeki yanlış prose
  cümlesi hâlâ yanlış — belge aracı örneği sınar, açıklamayı değil.
- Aynı işlev aynı anda iki güvence sınıfı taşıyabilir: belgesinin düzyazısı
  denetlenmiyor, belgesinin örneği sürdürüyor.

#### Sonraki Adım

Proje Yaşam Döngüsü konusunun bu dört dersi, derleyicinin güvencesinin dört ayrı
yerde nasıl durduğunu gösterdi: kapalı bir bayrağın gövdesinde (denetlenmiyor),
koşarak kurulan bir sınamada (devredildi), sayılan bir işlem biriminde
(denetlenmiyor) ve şimdi bir belge cümlesinde — burada ilk kez tek bir derste
iki sınıf (denetlenmiyor ve sürdürüyor) bir arada. Dördü de aynı gerçeğin farklı
görünümleri: derleyicinin durduğu her yerde, kalan boşluğu ya hiç kimse
doldurmuyor, ya yazan taraf koşarak dolduruyor, ya da dilin kendi bir aracı
farklı bir denetimle devreye giriyor.

Kursun son dersi — kurs kapanışı — bu dört yeri bir araya getirip beşinci bir yere
bakıyor: bir kaynağın dışına, bir sürüm dizesine. O dize de kaynakta duruyor ve
hiçbir şeye karşılık gelmiyor; bir kitaplığın hangi sürümünün hangi imzayı
koruduğu, yayımlama dersinin konusu. Bu ders orada da izini bırakıyor: bir sürüm
dizesi de tıpkı bir belge cümlesi gibi kaynakta duran, insanın okuduğu ama
derleyicinin hiç denetlemediği bir iddia.

### Yayımlama

Source: https://academia.sh/tr/kurslar/rust-ileri/yayimlama

Bir sürüm dizesinin kaynakta durup gövdeyle hiçbir denetimden geçmediği, sürüm uyumunun imza düzeyinde modellendiği ve aynı imzanın farklı davranış verebildiği — sözün imzada durduğu, davranışta durmadığı — koşturularak gösterilir.

Belgelendirme dersi, bir belge örneğinin geçmesinin üstündeki açıklamanın doğru olduğu
anlamına gelmediğini gösterdi: belge aracı yalnız örneği sınıyor, üstündeki cümleyi hiç
okumuyordu. Bu son ders aynı ayrımı bir paketin dışa açtığı iki başka yüzeye taşıyor — bir
sürüm dizesi ve bir imza. İkisi de bir söz gibi görünür: sürüm dizesi "bu paket şu durumda"
der, imza "bu işlev şunu alır, şunu döndürür" der. Rust Ekosistemi ve İleri Konular kursu
boyunca on bir ders aynı soruyu on bir farklı düzenekte sordu — derleyicinin güvencesi
burada nerede bitiyor? — ve yanıt dört sınıftan birine düştü. Bu son ders o dört sınıfı iki
söze uyguluyor ve ikisinin de aynı sınıfa düşmediğini gösteriyor.

#### Sürüm Dizesinin Anlamı

Kursun açılışında koşturulan çekirdek, on iki gözlemden birini şuna ayırmıştı: kaynakta
duran bir sürüm dizesi hiçbir şeye karşılık gelmiyordu. Aşağıdaki koşum bu gözlemi bir paket
bildirimi ve onu paket kaydına gönderen bir işlevle genişletiyor — kayıt adı hiçbir yerde
yazılmıyor, gönderim yalnız modelleniyor.

```rust
// paket_bildirimi.rs — surum dizesi kaynakta duruyor, gonderimi modelleyen islev ona hic bakmiyor.
struct PaketBildirimi {
    ad: &'static str,
    surum: &'static str,
}

const SURUM_DIZESI: &str = "0.1.0";

fn iki_kat(x: i32) -> i32 {
    x * 2
}

// Gonderimi modelleyen islev: bildirimi aliyor, bir onay dizesi uretiyor.
// Surum alaninin icerigini hicbir bicimde denetlemiyor - herhangi bir dizgi kabul.
fn gonderimi_modelle(bildirim: &PaketBildirimi) -> String {
    format!("{} paketi govdeyi degistirmeden {} olarak isaretlendi", bildirim.ad, bildirim.surum)
}

fn main() {
    let bildirim = PaketBildirimi { ad: "olcum_kasasi", surum: SURUM_DIZESI };
    println!("{}", gonderimi_modelle(&bildirim));
    println!("iki_kat(21) = {}", iki_kat(21));

    let ikinci_bildirim = PaketBildirimi { ad: "olcum_kasasi", surum: "0.2.0" };
    println!("{}", gonderimi_modelle(&ikinci_bildirim));
    println!("iki_kat(21) = {}", iki_kat(21));
}
```

```
olcum_kasasi paketi govdeyi degistirmeden 0.1.0 olarak isaretlendi
iki_kat(21) = 42
olcum_kasasi paketi govdeyi degistirmeden 0.2.0 olarak isaretlendi
iki_kat(21) = 42
```

**PY16.** İki bildirim de aynı `iki_kat` işlevine işaret ediyor, ikisi de aynı çağrıyı
veriyor ve ikisi de `42` döndürüyor — ama `surum` alanı `"0.1.0"`den `"0.2.0"`ye değişti.
`gonderimi_modelle` işlevinin imzası, `surum` alanının hangi dizgiyi taşıdığına hiç
bakmıyor: `&'static str` tipindeki her değeri kabul ediyor. Kaynakta gövde tek bir karakter
değişmeden kalırken sürüm alanı elle bir üst basamağa taşınabiliyor ve derleyici bu ikisi
arasında hiçbir tutarlılık aramıyor.

Bu, kursun açılış çekirdeğindeki gözlemin birebir tekrarı: bir sürüm dizesi kaynakta duruyor
ve hiç kimse onu gövdeyle karşılaştırmıyor. Sınıf **denetlenmiyor** — ne derleyici, ne bir
araç, ne de koşan program sürüm alanının gövdeyle tutarlı olup olmadığını sınıyor. Bir paket
kaydına gönderim de aynı sınırı taşır: kayıt, gönderilen bir sürüm dizesini kabul eder, ama
o dizenin gövdedeki değişikliği doğru yansıttığını denetlemez — bu denetim tamamen yazan
tarafın kararına kalır.

#### Sürüm Uyumluluğu ve İmza

**Ayrım cümlesi:** M09/K05 (Üretim Go) kursu modül yayımlamayı ve sürüm uyumunu bilginin
kaynağı ekseninde ölçtü — bir sürüm bildiriminin kaynaktan mı, araçtan mı, koşumdan mı
geldiği soruluyordu. Bu derste sorulan farklı: bir sürüm bildirimi değil, bir imza söz
konusu olduğunda derleyicinin kendi denetiminin nerede durduğu.

Sürüm dizesi hiçbir denetimden geçmiyordu, ama bir işlevin imzası öyle değil. Aşağıdaki
koşum aynı çağıran kaynağı üç ayrı kütüphane sürümüne karşı derliyor: birincisi temel sürüm,
ikincisi aynı imzayla ama farklı bir gövdeyle yazılmış bir sonraki sürüm, üçüncüsü imzası
değişmiş bir sürüm. Redd program içinden ölçülüyor — kaynaklar birer dizgi olarak
tutuluyor, derleyici `std::process::Command` ile çağrılıyor, yalnız derlendi/derlenmedi
basılıyor, hata metni hiç okunmuyor.

```rust
// surum_uyumu.rs — ayni cagirana karsi uc kutuphane surumu denenir, yalniz derlendi/derlenmedi basilir.
use std::env;
use std::fs;
use std::process::Command;

const CAGIRAN: &str = r#"
fn main() {
    let sonuc = donustur(10);
    println!("sonuc={}", sonuc);
}
"#;

const SURUM_BIR: &str = "fn donustur(x: i32) -> i32 { x + 1 }\n";
const SURUM_IKI_AYNI_IMZA: &str = "fn donustur(x: i32) -> i32 { x * 2 }\n";
const SURUM_IKI_DEGISEN_IMZA: &str = "fn donustur(x: i32, adim: i32) -> i32 { x + adim }\n";

fn derlenir_mi(kaynak: &str, calisma: &std::path::Path, ad: &str) -> bool {
    let yol = calisma.join(format!("{}.rs", ad));
    fs::write(&yol, kaynak).unwrap();
    let ikili = calisma.join(ad);
    Command::new("rustc")
        .args(["--edition", "2021"])
        .arg("-o").arg(&ikili)
        .arg(&yol)
        .output()
        .map(|c| c.status.success())
        .unwrap_or(false)
}

fn siniflandir(basarili: bool) -> &'static str {
    if basarili { "derlendi" } else { "derlenmedi" }
}

fn main() {
    let calisma = env::temp_dir().join(format!("surum-uyumu-{}", std::process::id()));
    fs::create_dir_all(&calisma).unwrap();

    for (ad, govde) in [
        ("surum_bir", SURUM_BIR),
        ("surum_iki_ayni_imza", SURUM_IKI_AYNI_IMZA),
        ("surum_iki_degisen_imza", SURUM_IKI_DEGISEN_IMZA),
    ] {
        let birlesik = format!("{}{}", govde, CAGIRAN);
        let basarili = derlenir_mi(&birlesik, &calisma, ad);
        println!("{}: {}", ad, siniflandir(basarili));
    }

    let _ = fs::remove_dir_all(&calisma);
}
```

```
surum_bir: derlendi
surum_iki_ayni_imza: derlendi
surum_iki_degisen_imza: derlenmedi
```

**PY17.** Aynı çağıran kaynağı — `donustur(10)` çağrısı ve sonucu yazdıran satır — üç
sürüme karşı hiç değişmeden kaldı. Birinci ve ikinci sürüme karşı derlendi, çünkü ikisi de
çağıranın beklediği imzayı taşıyordu; üçüncü sürüme karşı derlenmedi, çünkü üçüncü sürüm
ikinci bir parametre eklemişti ve çağıran tek bir bağımsız değişken geçiriyordu. Derleyici
burada gerçekten bir sürüm uyumu sınadı — ama sınadığı şey sürüm numarası değil, imza.

Bu, sürüm dizesinin denetlenmiyor sınıfına düşmesiyle doğrudan karşıtlık kuruyor. Sınıf
burada **sürdürüyor**: derleyici, bir kütüphanenin çağırana karşı hâlâ tip uyumlu olup
olmadığını her derlemede yeniden sınıyor ve bu sınama sürüm numarasının kendisinden
tamamen bağımsız — ikinci sürüm birinci sürümle aynı numarayı taşısaydı da, farklı bir
numara taşısaydı da sonuç değişmezdi, çünkü derleyici sürüm alanına hiç bakmıyor, yalnız
imzaya bakıyor.

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

Derleyicinin imzaya bakması, çağıranın hangi sürüme karşı derlenmeye devam ettiğini
gerçekten sınıyor — ama bu sınama davranışa hiç uzanmıyor. Aşağıdaki koşum, tam olarak aynı
imzayı taşıyan iki sürümü yan yana çağırıyor.

```rust
// imza_ile_davranis.rs — ayni imza farkli sonuc verebilir; soz imzada, davraniste degil.
fn donustur_v1(x: i32) -> i32 {
    x + 1
}

fn donustur_v2(x: i32) -> i32 {
    x * 2
}

fn cagiran<F: Fn(i32) -> i32>(donustur: F, girdi: i32) -> i32 {
    donustur(girdi)
}

fn main() {
    let girdi = 10;
    println!("v1: {}", cagiran(donustur_v1, girdi));
    println!("v2: {}", cagiran(donustur_v2, girdi));
}
```

```
v1: 11
v2: 20
```

**PY18.** `donustur_v1` ve `donustur_v2` birebir aynı imzayı taşıyor; `cagiran` işlevinin
genel sınırı ikisini de aynı biçimde kabul ediyor. Ama aynı girdiyle çağrıldıklarında ikisi
bambaşka bir sonuç veriyor: biri `11`, öbürü `20`. Bir önceki bölümdeki ölçüm bu farkı hiç
yakalamazdı — orada sınanan yalnız "çağıran hâlâ derleniyor mu" sorusuydu ve bu soru her
iki sürüm için de "evet" yanıtını verirdi.

Bu sınır, kursun ilk dersindeki gözlemin bir yankısı: orada bir makro, iki anlamca uzak
türle çağrıldığında ikisini de derliyordu, çünkü sözdizimi düzeyinde tür uygunluğuna
bakıyordu, anlama bakmıyordu. Burada aynı imza, iki anlamca uzak gövdeyi taşıyor ve
derleyici ikisini de kabul ediyor, çünkü imza düzeyinde tip uygunluğuna bakıyor, davranışa
bakmıyor. Söz — sürüm uyumlu olduğunu söylemenin verdiği güvence — imzada duruyor;
davranışta durmuyor. Bir paket kaydına yeni bir sürüm gönderildiğinde, derleyicinin bu
sürümü kabul etmesi yalnız imzanın tutarlı kaldığını gösterir, gövdenin öncekiyle aynı işi
yaptığını göstermez.

#### Kapanış Tablosu

| Ders | Ölçülen şey | Güvencenin sınıfı | Sınırlayıcı ölçüm |
|---|---|---|---|
| Bildirimsel Makrolar | Makronun ürettiği gövdenin tip denetiminden geçmesi, makro hijyeni | sürdürüyor | Makro yalnız tür uygunluğuna bakar; `i32` ve `bool` ile çağrılan aynı makro ikisinde de derlenir |
| Prosedürel Makrolar | Üretimin ayrı bir derleme adımında da denetlenmesi | sürdürüyor | Prosedürel makro kendi kasasında derlenmek zorunda — tercih değil, dil kısıtı |
| Alan Özel Diller | Makro çıktısının bir arayüzde de yine denetlenmesi | sürdürüyor | Okunaklı yazım doğruluk getirmez; yanlış bir yol-işlev bağı da tür uyunca derlenir |
| `unsafe` Rust | Aynı blokta bir işlemin denetimsiz, öbürünün denetimli kalması | sürdürüyor · devredildi | Devredilen güvence doğru kullanıldığında görünmez; fark yanlış kullanımın sorumluluğunda |
| Yabancı Fonksiyon Arayüzü | İki C işlevinin yalnız imzasına güvenilerek çağrılması | devredildi · dilin dışında | Bildirim yanlış yazılsaydı derleyici bunu fark edemezdi |
| WebAssembly Hedefi | Hedefe göre değişen kararın kaynakta ayrılması | dilin dışında | Kaynağın taşınabilir görünmesi taşınabilir olduğu anlamına gelmez |
| Gömülü Hedefler | Standart kütüphanesiz ortamın ayırma ve panik davranışını elden alması | dilin dışında | Ayırmasız yazım her zaman mümkün değildir; sabit tampon üst sınır ister |
| Bağımlılık ve Özellik Bayrakları | Kapalı bir bayrağın gövdesinin derlemeye hiç girmemesi | denetlenmiyor | Bugün derlenmediğini gösterir, bayrak açıldığında derlenip derlenmeyeceğini göstermez |
| Test ve Özellik Tabanlı Sınama | Bir özelliğin sabit tohumlu bir üreteçle sınanması | devredildi | Geçen sınama yalnız sınanan girdi kümesi için doğrudur; aralık dışı hiç sınanmaz |
| Karşılaştırmalı Ölçüm ve Profilleme | İki sıralama yazımının karşılaştırma ve kopyalama sayısı | denetlenmiyor | İşlem sayısı süre demek değildir; az işlem yapan bir yazımın süresi gerçekte daha uzun olabilir |
| Belgelendirme | Belge örneğinin sınanması, üstündeki açıklamanın sınanmaması | sürdürüyor · denetlenmiyor | Belge aracı örneği sınar, açıklamayı değil; aynı işlev iki sınıfı birden taşıyabilir |
| Yayımlama | Sürüm dizesinin anlamı ve sürüm uyumunun imza düzeyinde sınanması | denetlenmiyor · sürdürüyor | Aynı imza farklı davranış verebilir — söz imzada durur, davranışta durmaz |

#### Özet

- Sürüm dizesi kaynakta duruyor ve gövdeyle hiçbir denetimden geçmiyor; sınıf
  **denetlenmiyor** — sürüm bildirimini paket kaydına gönderen model bile sürüm alanının
  içeriğine bakmıyor.
- Aynı çağıran kaynağı üç kütüphane sürümüne karşı derlendi: imzası aynı kalan iki sürüme
  karşı derlendi, imzası değişen sürüme karşı derlenmedi; sınıf **sürdürüyor** — derleyici
  sürüm numarasına değil, imzaya bakıyor.
- Aynı imzayı taşıyan iki sürüm birebir aynı girdiyle çağrıldığında farklı sonuç verdi
  (`11` ile `20`); imza düzeyindeki sürüm uyumu denetimi davranışa hiç uzanmıyor — söz
  imzada duruyor, davranışta durmuyor.
- Kurs boyunca dört sınıf değişmedi: sürdürüyor, devredildi, dilin dışında, denetlenmiyor;
  on iki gözlemin dağılımı 4/3/2/3'tü ve bu son ders o dağılımın hem sürdürüyor hem
  denetlenmiyor uçlarına birer örnek ekledi.
- Paket kaydına gönderim burada gerçek bir ağ çağrısı taşımadan, yalnız bir bildirim ve
  onu kabul eden bir işlevle modellendi; kayıt adı hiçbir yerde yazılmadı.

#### Kurs Kapanışı

Rust Ekosistemi ve İleri Konular kursu on iki ders boyunca tek bir soruyu on iki farklı
düzenekte sordu: derleyicinin güvencesi burada nerede bitiyor? Yanıt dört sınıftan birine
düştü ve bu dört ad kurs boyunca değişmedi — sürdürüyor, devredildi, dilin dışında,
denetlenmiyor; on iki gözlemin dağılımı dört, üç, iki, üçtü. Makrolar konusu üretilen
kaynağın sıradan kaynak gibi denetlendiğini, ama bu denetimin yalnız tür düzeyinde
çalıştığını gösterdi. Düşük Seviye ve Birlikte Çalışma konusu denetimin nerede yazana ya da
bir başka dile devredildiğini ve devredilen güvencenin doğru kullanıldığında görünmez
kaldığını gösterdi. Proje Yaşam Döngüsü konusu, kaynağın kimi zaman hiç denetlenmeyen
köşeleri olduğunu — kapalı bir bayrağın gövdesi, bir işlem sayısının süreye çevrilmesi, bir
belge cümlesinin doğruluğu, bir sürüm dizesinin anlamı — art arda gösterdi. Bu son ders iki
köşeyi bir arada yerleştirdi: bir sürüm dizesi hiç denetlenmiyor, ama aynı sürümün imzası
hâlâ denetleniyor.

Rust müfredatı beş kursla kapanıyor ve her biri aynı dile bambaşka bir soru sordu. Rust
Temelleri, yazılan bir satırın bir değer mi ürettiğini sordu — ifade, deyim, öğe ayrımı.
Sahiplik ve Bellek Modeli, o değerin ömrünün nerede bittiğini sordu. Trait'ler, Genellikler
ve Hata Yönetimi, bir gövdenin kaç kez koştuğunu sordu. Eşzamanlılık ve Asenkron Rust, bir
işin ilerlemek için neye ihtiyaç duyduğunu sordu. Rust Ekosistemi ve İleri Konular,
derleyicinin güvencesinin nerede bittiğini sordu. Beş soru aynı dilin beş farklı katmanına
baktı: önce yazılan satırın kendisi, sonra kaynaktaki bir değerin ömrü, sonra bir gövdenin
kaç kez koştuğu, sonra bir işin ilerlemek için neye muhtaç olduğu, son olarak da bütün
bunları sağlayan denetimin kendisinin nerede durduğu.

Sıradaki müfredat C ve C++'a geçiyor; bu iki dil için hangi ölçü ekseninin seçileceği bu
derste henüz belli değil.

