---
title: 'Gömülü Hedefler'
source: 'https://academia.sh/tr/kurslar/rust-ileri/gomulu-hedefler'
course: 'Rust Ekosistemi ve İleri Konular'
language: tr
updated: '2026-09-13T05:09:27+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Yazdırma hizmetinin işletim sistemine dayandığını, bu bağımlılığın koşullu derlemeyle kaynakta nasıl işaretlendiğini gösterir'
    - 'Ayırma yapan bir gövde ile sabit boyutlu bir tamponla çalışan bir gövdenin aynı sonucu ürettiğini karşılaştırarak kanıtlar'
    - 'Sabit boyutlu bir tamponun kapasite sınırını aşan bir girdide neden yetersiz kaldığını örnekle açıklar'
---

# Gömülü 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.
