Tüm Rust rehberleri
TEKNİK REHBER TEMEL GEÇİŞ 2026

C/C++'tan Geçiş
Kavram eşlemesi ve tuzaklar

C/C++ kavramlarının Rust karşılığı, kas hafızası tuzakları ve zihniyet değişimi; nereden başlamalı — deneyimli sistem programcısı için hızlandırılmış harita.

00 Zihniyet değişimi

Rust'ı C++ gibi yazmaya çalışırsan derleyici sürekli yolunu keser; doğru hamle borrow checker'ı bir düşman değil, bir tasarım ortağı olarak görmektir.

C/C++ dünyasından gelen herkesin ilk haftalarda yaşadığı duygu aynıdır: derleyiciyle güreşmek. Kafandaki algoritma nettir, C'de beş dakikada yazardın, ama Rust derleyicisi borrow of moved value, cannot borrow as mutable, does not live long enough diye art arda reddediyor. Bu noktada iki yol var: ya hataları susturmak için rastgele .clone(), Rc<RefCell> ve unsafe serpiştirirsin, ya da derleyicinin sana ne söylemeye çalıştığını dinlersin. İkinci yol acı verir ama tek doğru yoldur.

Zihniyet farkını tek cümleyle özetlemek gerekirse: C++'ta kod yazarken belleği düşünürsün; Rust'ta tasarım yaparken sahipliği düşünürsün. C++'ta bir nesneyi kim siler, kim sahiplenir, kim sadece bakar — bunları kafanda ya da yorum satırlarında tutarsın ve hata yaptığında runtime'da öğrenirsin. Rust'ta aynı soruların cevabını tip imzasına yazarsın: fn f(x: T) sahipliği alır, fn f(x: &T) ödünç bakar, fn f(x: &mut T) ödünç değiştirir. İmza bir sözleşmedir ve derleyici onu zorlar.

C++ refleksi              Rust yaklaşımı
─────────────             ─────────────
kodu yaz → derle →        sahipliği tasarla →
crash'i debug et          derleyici doğrular → çalışır

Borrow checker'ı bir linter ya da bir hata bulucu sanmak en yaygın yanılgıdır. O bir doğrulayıcıdır: sen veri akışını ve ömürleri doğru tasarladıysan onaylar, tasarlamadıysan reddeder. Yani borrow checker hatası genelde "şu satırı düzelt" değil, "bu verinin sahibi kim olmalı, ömrü neye bağlı olmalı" sorusunu yeniden sormanı söyler. Deneyimli Rust geliştiricileri bu yüzden borrow checker'la neredeyse hiç güreşmez — çünkü kodu yazmadan önce sahipliği kafalarında çözmüşlerdir.

NOT

İlk iki-üç hafta direnç hissetmek tamamen normaldir; bu C++ reflekslerinin Rust modeline çarpmasıdır, beceriksizlik değil. Bu rehberdeki tuzakların çoğu "C'de doğru olan alışkanlığın Rust'ta neden yanlış reflekse dönüştüğünü" anlatır. İlgili ownership rehberi bu modelin teorik temelini verir; burada odak geçiş psikolojisi ve pratik eşlemedir.

DİKKAT

İlk haftalarda her derleyici hatasını .clone() veya .unwrap() ile susturma refleksine düşme. Bu kısa vadede ilerletir ama Rust'ı öğrenmez, sadece "çirkin C++" yazmış olursun. Hatayı sustur değil, anla.

Bu bölümde

  • Geçişin özü "derleyiciyle güreşmek"ten "derleyiciyle tasarlamak"a kaymaktır.
  • C++'ta belleği yazarken, Rust'ta sahipliği tasarlarken düşünürsün; cevaplar tip imzasına yazılır.
  • Borrow checker bir hata bulucu değil, tasarım kararlarını doğrulayan bir araçtır.
  • İlk haftalardaki direnç normaldir; hatayı susturmak değil anlamak öğretir.

01 Büyük kavram eşleme tablosu

Çoğu C/C++ kavramının Rust'ta doğrudan bir karşılığı vardır; ama eşleme birebir değil — bazı kavramlar tek bir Rust özelliğine, bazıları ise dile içkin bir varsayılana karşılık gelir.

Aşağıdaki tablo bu rehberin omurgasıdır. Bir kavramı ararken buraya dön; her satır sonraki bölümlerde ya da ilgili rehberlerde derinleştirilir. Önemli olan nüansı görmek: bazı C++ kavramları (örn. const, kopya semantiği) Rust'ta artık varsayılan davranış olduğu için "özel bir özellik" olmaktan çıkmıştır.

C / C++RustNot
T* (sahipsiz pointer)&T / &mut TReferans; ömrü derleyici tarafından izlenir.
new T / unique_ptr<T>Box<T>Tekil sahipli heap tahsisi. İlgili smart pointer rehberi.
malloc / freeownership + DropSerbest bırakma kapsam sonunda otomatik.
delete(otomatik) / drop(x)Elle free neredeyse hiç gerekmez.
std::vector<T>Vec<T>Büyüyen dizi; sahiplik Vec'te.
T* raw (bilinçli ham)*const T / *mut TSadece unsafe içinde dereference edilir.
classstruct + implVeri ve davranış ayrı bloklarda.
virtual / saf sanal sınıftrait + dyn TraitDinamik dağıtım açık biçimde belirtilir.
template<typename T>generic <T> + trait boundMonomorfizasyon; kısıtlar imzada. İlgili trait rehberi.
#define SABIT 42const SABIT: i32 = 42;Tipli, kapsamlı; ön-işlemci yok.
#define makro hilesimacro_rules! / proc-macroHijyenik, AST düzeyinde makrolar.
header (.h) / #includemod + useHeader/impl ayrımı yok; modül sistemi.
namespacemodİsim alanı ve modül aynı mekanizma.
throw / try/catchResult<T, E> + ?Hata bir değer; tip imzasında görünür.
errno / dönüş koduResult<T, E>Yan kanal yok; hata dönüş değerinde.
enum (C: sade tamsayı)enum (data taşıyabilir)Rust enum'ı = etiketli birleşim (tagged union).
unionenum (güvenli) / union (unsafe)Genelde istediğin şey enum'dur.
NULL / nullptrOption<T> (None)"Yokluk" tip seviyesinde; null dereference imkânsız.
const T(varsayılan immutable)Değişkenler varsayılan değişmez; mut ile aç.
std::move(x)(varsayılan move)Atama/geçiş zaten move; std::move gibi bir araç gerekmez.

İki satır özellikle dikkat ister. NULL → Option: Rust'ta "bir değer olmayabilir" durumu tipte kodlanır, dolayısıyla null pointer dereference diye bir hata kategorisi yoktur. const → varsayılan immutable: C++'ta değişkenleri korumak için const eklersin; Rust'ta tam tersi — her şey değişmezdir, değiştirmek istediğinde mut eklersin. Bu tek kelimelik tersine çevirme, kodun büyük kısmını otomatik olarak daha güvenli yapar.

NOT

Rust enum'ı C enum'undan çok daha güçlüdür: her varyant veri taşıyabilir, bu da onu C++'taki std::variant ya da elle yazılmış tagged union'ın yerine koyar. Option ve Result de aslında birer enum'dur. İlgili enum & pattern matching rehberi bunu derinleştirir.

Bu bölümde

  • Çoğu C/C++ kavramının net bir Rust karşılığı var; ama eşleme birebir değil, nüanslı.
  • Pointer'lar referans/Box/ham pointer'a, sınıflar struct+impl'e, virtual'lar trait+dyn'e eşlenir.
  • const ve move artık özellik değil, dilin varsayılanı — refleksi tersine çevir.
  • NULL → Option ve enum'ın veri taşıması bütün bir hata kategorisini ortadan kaldırır.

02 Bellek modeli farkı

C++'ta atama varsayılan olarak kopyalar (kopya ctor), Rust'ta atama varsayılan olarak taşır (move) — bu tek fark birçok refleksini tersine çevirir.

C++'ta bir nesneyi başka bir değişkene atadığında, aksini belirtmedikçe kopya yapıcı (copy constructor) çağrılır. Büyük bir vektörü farkında olmadan kopyalamak, C++'ta klasik performans tuzağıdır; bu yüzden std::move ve referansları öğrenirsin. Rust'ta varsayılan tersidir: atama taşır (move), kopyalamaz. Taşınan değer artık kullanılamaz — eski sahip "boşaltılır".

c++
// C++: atama varsayılan olarak KOPYALAR
std::vector<int> a = {1, 2, 3};
std::vector<int> b = a;   // derin kopya — a hâlâ geçerli, sessiz maliyet
a.push_back(4);           // b etkilenmez
rust
// Rust: atama varsayılan olarak TAŞIR (move)
let a = vec![1, 2, 3];
let b = a;        // sahiplik b'ye taşındı — kopya YOK
// a.push(4);     // HATA: borrow of moved value: `a`
let c = b.clone(); // kopya istiyorsan AÇIKÇA söyle

Buradaki kazanım şudur: Rust'ta hiçbir derin kopya sessizce oluşmaz. Kopyalamak istiyorsan .clone() yazarsın ve maliyet kodda görünür hale gelir. i32, bool, char gibi küçük, sabit boyutlu tipler Copy trait'ini uygular ve gerçekten kopyalanır — ama bunlar bit kopyası olduğu için ucuzdur. Heap tutan her şey (String, Vec, Box) move semantiğindedir.

C / C++RustNot
kopya ctor (varsayılan)move (varsayılan)Atama eski değeri geçersiz kılar.
std::move(x)(zaten move)Ekstra araca gerek yok.
kopya = auto b = a;let b = a.clone();Kopya artık açık ve aranabilir.
RAII (destructor, elle yaz)Drop (otomatik üretilir)Alanlar ters sırada otomatik drop edilir.
moved-from nesne (geçerli ama belirsiz)moved-from = kullanılamazDerleyici erişimi engeller.

RAII tarafında Rust C++'tan daha katıdır ve daha rahattır. C++'ta destructor'ı elle yazarsın ve "rule of three/five" gibi kuralları akılda tutarsın. Rust'ta Drop derleyici tarafından üretilir: bir değer kapsamdan çıktığında alanları otomatik olarak, tanımlanma sırasının tersinde drop edilir. Özel temizlik gerekirse impl Drop yazarsın — ama Drop uygulayan bir tip artık Copy olamaz, çünkü "iki kopya, iki temizlik" çelişkisi olurdu.

DİKKAT

C/C++'ta sıkça yazdığın kendi kendine referans veren yapılar (bir struct'ın bir alanı aynı struct'ın başka alanına pointer tutar) Rust'ta doğrudan ifade edilemez. Çünkü değer taşındığında adres değişir ama içerideki pointer eskisini gösterir — bu tam olarak Rust'ın yasakladığı dangling durumudur. Çözüm: index kullan, Rc/arena tahsisi yap ya da gerçekten gerekiyorsa Pin ve unsafe'e in. Çoğu durumda tasarımı yeniden düşünmek doğru cevaptır.

Bu bölümde

  • C++ varsayılanı kopya, Rust varsayılanı move — sessiz derin kopya Rust'ta oluşmaz.
  • Kopya istiyorsan .clone() ile açıkça belirtirsin; maliyet kodda görünür.
  • Drop derleyici tarafından garanti edilir; destructor'ı elle yazmazsın.
  • Kendine referans veren yapılar move modeliyle çeliştiği için zordur; tasarımı yeniden düşün.

03 Sık refleks tuzakları — bellek/erişim

C'deki en sağlam alışkanlıkların bir kısmı Rust'ta ya gereksizdir ya da doğrudan borrow checker'a çarpar; bunları erken fark etmek aylarca zaman kazandırır.

İlk tuzak: index'li manuel döngü. C'de for (int i = 0; i < n; i++) refleksi kasına işlemiştir. Rust'ta bu hem deyimsiz (idiomatic değil) hem de bound-check maliyetli kalır. Iterator kullan — derleyici sınır kontrolünü çoğunlukla eler ve kod daha okunur olur.

c++
// C++ refleksi: index'li manuel döngü
std::vector<int> v = {1, 2, 3};
int sum = 0;
for (size_t i = 0; i < v.size(); i++)
    sum += v[i];   // her erişimde manuel index
rust
// Rust deyimi: iterator
let v = vec![1, 2, 3];
let sum: i32 = v.iter().sum();       // niyet net, sınır kontrolü elenir
// ya da: for x in &v { ... }

İkinci tuzak: paylaşımlı değiştirilebilir erişim. C'de iki pointer aynı tampona yazabilir; bu da aliasing bug'larının kaynağıdır. Rust'ın kuralı kemik gibi sağlam: aynı anda ya çok sayıda &T (salt-okunur) ya da tek bir &mut T (değiştirilebilir) olabilir — ikisi bir arada olamaz. Bu yüzden C'de doğal gelen "elimde iterator varken aynı koleksiyonu değiştireyim" hamlesi Rust'ta derlenmez (ve iyi ki derlenmez — C++'ta iterator invalidation tam olarak budur).

&TPaylaşımlı ödünç — istediğin kadar olabilir, hiçbiri yazamaz.
&mut TTekil değiştirilebilir ödünç — aynı anda yalnızca bir tane, başka ödünç yokken.
&T + &mut TAynı anda yasak — aliasing'i derleme zamanında engeller.

Üçüncü tuzak: getter/setter refleksi. C++'ta her alana get_x()/set_x() yazmak yaygındır. Rust'ta alanları doğrudan açmak (pub) ya da değişmez yapı + builder kullanmak çoğu zaman daha deyimseldir; getter/setter sadece gerçekten invariant koruman gerektiğinde yazılır. Encapsulation modül seviyesinde de sağlanır.

Dördüncü tuzak: "yokluk için ham pointer arama". C'de "bulunamadı"yı NULL döndürerek anlatırsın. Rust'ta bu Option<&T>'dir; null kontrolünü unutamazsın çünkü Option'ı açmadan içindeki değere erişemezsin.

C / C++ refleksiRust deyimiNot
for (i=0; i<n; i++) v[i]v.iter() / for x in &vSınır kontrolü elenir, niyet açık.
iki pointer aynı tampona yazar&mut tekliğiAliasing derleme zamanında yasak.
döngüde koleksiyonu değiştirtopla/filtrele, sonra uygulaIterator invalidation imkânsız.
get_x()/set_x() her alanapub alan / builderGetter sadece invariant için.
"bulunamadı" → NULLOption<&T>Kontrol atlanamaz.
NOT

"Döngüde koleksiyonu değiştiremiyorum" engeline takılırsan deyim şudur: önce iter().filter().collect() ile değişecekleri topla, döngü bitince uygula. Borrow checker burada seni iterator invalidation'dan koruyor — direnme, kalıbı değiştir. İlgili koleksiyonlar & iterator rehberi bu kalıpları detaylandırır.

Bu bölümde

  • Index'li manuel döngü yerine iterator kullan; daha hızlı ve daha okunur.
  • Paylaşımlı değiştirilebilir erişim yasaktır: ya çok &T ya tek &mut T.
  • Otomatik getter/setter refleksini bırak; pub alan ya da builder çoğu zaman daha deyimsel.
  • "Yokluk için NULL" yerine Option; null kontrolü atlanamaz hale gelir.

04 Araç ve build eşlemesi

Rust'ın en büyük günlük konforu entegre araç zinciridir: build, bağımlılık, test, formatlama ve lint tek bir komutun (cargo) altında toplanır.

C/C++'ta build sistemi seçmek başlı başına bir projedir: Makefile mi, CMake mi, Meson mi; bağımlılıkları nasıl çekeceğin (vcpkg, Conan, submodule) ayrı bir tartışma. Rust'ta bunların hepsi cargo ve Cargo.toml ile gelir. Bağımlılık eklemek tek satırdır, build/test/çalıştırma tek komuttur. Bu, geçişte hissedeceğin ilk somut rahatlamadır.

C / C++RustNot
make / cmake / Mesoncargo buildBuild + bağımlılık + test tek araçta.
vcpkg / Conan / submoduleCargo.toml + crates.ioBağımlılık tek satır. İlgili cargo rehberi.
gcc / clangrustc (cargo çağırır)rustc'yi elle çağırman nadir.
gdb / lldbaynısı + rust-gdb/rust-lldbSarmalayıcılar tipleri güzel basar.
Valgrind / ASan / UBSanMiri + sanitizer'larMiri, unsafe'te UB'yi yakalar.
clang-tidy / cppcheckcargo clippy500+ lint; deyimsel kodu öğretir.
clang-formatcargo fmt (rustfmt)Tek standart stil; tartışma biter.
ayrı test framework (gtest)cargo test (dahili)#[test] ile, harici bağımlılık yok.
Doxygencargo doc (rustdoc)Doc yorumları test bile edilir.

Günlük döngüde en çok dokunacağın iki araç: cargo clippy ve cargo fmt. Clippy sadece bug değil, deyim de öğretir — "burada iter().sum() kullan", "bu match aslında if let olabilir" gibi öneriler verir. Geçiş döneminde clippy'yi sürekli çalıştırmak, C++ reflekslerini Rust deyimlerine çevirmenin en hızlı yoludur.

cargo fmt → cargo clippy → cargo test → cargo build --release
NOT

Hata ayıklarken rust-gdb ya da rust-lldb kullan: bunlar standart gdb/lldb'nin üstüne Rust tip yazıcıları (pretty-printer) ekler, böylece Vec ve String ham bellek yerine okunur biçimde görünür. unsafe kod yazıyorsan, çalışma zamanında UB'yi yakalamak için cargo +nightly miri test paha biçilmezdir — Valgrind'in soyut yorumlayıcı muadili.

Bu bölümde

  • cargo build, bağımlılık, test, doc ve çalıştırmayı tek araçta birleştirir.
  • Debugger aynı (gdb/lldb) ama rust-gdb/rust-lldb tipleri okunur basar.
  • Valgrind/ASan karşılığı Miri + sanitizer'lar; clang-tidy → clippy, clang-format → rustfmt.
  • Clippy geçiş döneminde deyim öğreten en değerli araçtır; sürekli çalıştır.

05 Anti-pattern'ler ve deyimler

"C'de böyle yapardım" cümlesi geçiş sürecinin en tehlikeli cümlesidir; her C kalıbının Rust'ta daha temiz, daha güvenli bir deyimsel karşılığı vardır.

İlk anti-pattern: çıkış parametresi (output parameter). C'de bir fonksiyon hem başarı durumunu hem sonucu döndüremez, bu yüzden sonucu pointer ile dışarı yazarsın. Rust'ta birden çok değer döndürmek serbesttir — tuple ya da daha iyisi Result kullan.

c++
// C: çıkış parametresi + hata kodu
int parse(const char *s, int *out) {
    if (!valid(s)) return -1;   // hata kodu
    *out = atoi(s);               // sonuç dışarı yazılır
    return 0;
}
rust
// Rust: sonuç ve hata tek dönüş değerinde
fn parse(s: &str) -> Result<i32, ParseError> {
    if !valid(s) {
        return Err(ParseError::Invalid);
    }
    Ok(s.parse()?)   // başarı da hata da tipte görünür
}

İkinci anti-pattern: OOP kalıtım hiyerarşisi. C++'ta davranış paylaşmak için temel sınıftan türetirsin. Rust'ta kalıtım yoktur; davranışı trait ile soyutlar, kodu kompozisyonla paylaşırsın. Bu başta kısıtlayıcı gelir ama "elmas kalıtım", "kırılgan temel sınıf" gibi C++ dertlerini tamamen ortadan kaldırır.

C++ kalıtım                Rust kompozisyon
─────────                  ──────────────
Base → Derived             trait Davranış  +  struct { alanlar }
(is-a, sıkı bağ)           (has-a / does, gevşek bağ)

Üçüncü anti-pattern: paylaşımlı değiştirilebilir global. C'de global bir değişkeni her yerden okuyup yazmak yaygındır. Rust'ta değiştirilebilir global (static mut) unsafe'tir ve istenmez. Deyim: durumu parametre olarak geçir, ya da gerçekten global lazımsa OnceLock / LazyLock ile bir kez ilklenen, paylaşımlı (mümkünse immutable) bir değer kullan.

"C'de böyle yapardım"Rust deyimiNot
çıkış parametresi (int *out)tuple / Result dönüşüBirden çok değer dönmek serbest.
hata kodu döndürResult<T, E>Hata kullanılmadan göz ardı edilemez.
OOP kalıtım (Base/Derived)trait + kompozisyonElmas/kırılgan temel sınıf yok.
paylaşımlı değiştirilebilir globalOnceLock / parametre geçişistatic mut = unsafe, kaçın.
void* + castgeneric <T> / enumTip güvenli; cast cehennemi yok.

Dördüncü anti-pattern: void*. C'de tip silmek için void* taşır, sonra cast edersin — derleyici seni hiç korumaz. Rust'ta iki temiz alternatif var: tip parametresi gerçekten serbestse generic kullan; sonlu bir tip kümesi taşıyorsan enum kullan. Gerçekten heterojen koleksiyon lazımsa Box<dyn Trait> ile tip güvenli dinamik dağıtım yaparsın.

DİKKAT

Kalıtım refleksiyle her şeyi Box<dyn Trait> yapma. Çoğu zaman ihtiyacın generic + trait bound'dur (statik dağıtım, sıfır maliyet). dyn'ı sadece gerçekten heterojen bir koleksiyon ya da çalışma zamanı polimorfizmi gerektiğinde kullan. İlgili trait & generics rehberi bu seçimi ayrıntılandırır.

Bu bölümde

  • Çıkış parametresi ve hata kodu yerine tuple/Result dönüşü.
  • Kalıtım yerine trait + kompozisyon; elmas/kırılgan temel sınıf dertleri biter.
  • Değiştirilebilir global yerine OnceLock ya da durumu parametre geçirme.
  • void* yerine generic (serbest tip) ya da enum (sonlu küme); tip güvenliği korunur.

06 String dünyası

C/C++'ta char* ve std::string karmaşası Rust'ta net bir ikiliye oturur: sahipli String ve ödünç &str — ama UTF-8 ve FFI sınırında birkaç kuzen daha vardır.

C'de string'ler null-terminated char*'tır ve uzunluk, kodlama, sahiplik hep belirsizdir. C++ std::string sahipliği netleştirir ama hâlâ baytlar üzerinde çalışır. Rust string'leri iki temel tipte düşünür ve bu ayrım sahiplikle birebir örtüşür: String sahipli, büyüyebilir, heap'tedir (std::string gibi); &str ise bir string'in ödünç alınmış, değişmez bir dilimidir (bir const char* + uzunluk gibi, ama güvenli).

C / C++RustNe zaman
std::string (sahipli)StringSahiplik/değişiklik/büyüme gerektiğinde.
const char* + len&strSalt-okunur bakış; parametre tipi olarak ideal.
string literal "x"&'static strProgramla yaşayan sabit dilim.
C API'ye geçen char*CString / CStrNull-terminated, FFI sınırı için.
OS yolu / ortam değişkeniOsString / OsStrPlatforma özgü, UTF-8 garantisiz.

En kritik kural: fonksiyon parametresi her zaman &str olmalı, String değil. Çünkü &str hem bir String'i hem bir literal'i hem de bir dilimi kabul eder (deref coercion sayesinde), &String ise yalnızca String'i. Sahipliğe gerçekten ihtiyacın yoksa &str al.

c++
// C++: const ref alarak gereksiz kopyadan kaçın
void selam(const std::string& ad) {
    std::cout << "Merhaba " << ad;
}
rust
// Rust: &str al — String, literal ve dilim hepsi geçer
fn selam(ad: &str) {
    println!("Merhaba {ad}");
}
selam("Ada");                 // literal
selam(&String::from("Ada")); // String → &str (otomatik)

İkinci kritik fark: Rust string'leri UTF-8'dir, baytlar değil. Bu yüzden C'deki s[i] ile karakter indekslemek Rust'ta yoktur — çünkü bir "karakter" birden çok bayt olabilir. Bayt gezmek için .bytes(), Unicode skaler değer için .chars() kullanırsın. Bu başta sürpriz olur ama seni gizli kodlama bug'larından korur.

DİKKAT

Geçişin en yaygın çirkinleştirici tuzağı: borrow checker'ı susturmak için her yere .to_string() ve .clone() serpmek. Bu kod çalışır ama gereksiz tahsis yapar ve "C++'ı Rust sentaksıyla yazmak"tır. Çözüm genelde sahipliği değil ödünç almayı geçirmektir: String yerine &str al. Eğer gerçekten clone gerekiyorsa sorun yok — ama önce "ödünç alabilir miyim?" diye sor.

Bu bölümde

  • Temel ikili: sahipli String vs ödünç &str; FFI/OS için CString/OsString kuzenleri.
  • Fonksiyon parametresi her zaman &str olmalı — daha esnek ve kopyasız.
  • String'ler UTF-8'dir; s[i] indeksleme yok, .chars()/.bytes() kullan.
  • .to_string()/.clone() spam'ı bir tuzaktır; önce ödünç almayı dene.

07 Hata yönetimi zihniyeti

Rust'ta hata bir kontrol akışı kaçışı değil, bir değerdir; Result ve ? operatörü exception'ın okunabilirliğini, dönüş kodunun açıklığıyla birleştirir.

C'de hatalar dönüş koduyla (-1, errno) yayılır — açık ama gürültülü, ve kontrol etmeyi unutmak kolaydır. C++'ta exception'lar kodu temizler ama görünmez kontrol akışı yaratır: bir fonksiyonun fırlatıp fırlatmayacağını imzasından anlayamazsın. Rust üçüncü yolu seçer: hata bir değerdir (Result<T, E>) ve tip imzasında görünür, ama ? operatörü onu exception kadar zahmetsiz yayar.

c++
// C++: görünmez kontrol akışı — imza fırlatabileceğini söylemez
Config yukle(const std::string& yol) {
    auto ham = dosya_oku(yol);   // throw edebilir
    return ayristir(ham);         // bu da throw edebilir
}
rust
// Rust: hata imzada görünür, ? ile zahmetsiz yayılır
fn yukle(yol: &str) -> Result<Config, Error> {
    let ham = dosya_oku(yol)?;  // hata varsa erken döner
    let cfg = ayristir(&ham)?;  // zincir devam eder
    Ok(cfg)
}

En önemli zihinsel düzeltme: panic! bir exception DEĞİLDİR. C++'ta exception'lar normal, beklenen hata akışıdır ve yakalanır. Rust'ta panic! "programcı hatası / kurtarılamaz durum" anlamına gelir; varsayılan olarak thread'i sonlandırır ve genelde yakalamak için yazılmaz. Beklenen hatalar (dosya yok, geçersiz girdi, ağ kesildi) her zaman Result ile ifade edilir. panic!'i bir try/catch mekanizması gibi kullanmak Rust'ta anti-pattern'dir.

C / C++RustNot
dönüş kodu / errnoResult<T, E>Kontrol unutulamaz; tipte zorunlu.
throw / try-catchResult + ?Görünür akış, exception okunabilirliği.
throw (beklenen hata)Err(...)Beklenen hata panic değildir.
abort() / assertionpanic! / assert!Kurtarılamaz programcı hatası.
boş catch(...).unwrap() / .expect()Hatayı yutma; sadece prototip/test.
DİKKAT

Geçişin bir numaralı kötü alışkanlığı erken .unwrap() spam'ıdır: her Result'ı düşünmeden açıp programı potansiyel panic'lerle doldurmak. Prototipte tolere edilebilir, ama üretim kodunda her .unwrap() "burada çökebilirim" demektir. Bunun yerine hatayı ? ile yukarı yay, ya da en azından .expect("anlamlı mesaj") kullan. İlgili hata yönetimi rehberi ?, özel hata tipleri ve From dönüşümlerini ayrıntılı işler.

Bu bölümde

  • Hata bir değerdir (Result) ve imzada görünür; ? onu zahmetsiz yayar.
  • panic! exception değildir; beklenen hatalar her zaman Result ile ifade edilir.
  • Görünmez kontrol akışı (throw) yerine açık, tip güvenli akış elde edersin.
  • Erken .unwrap() alışkanlığından kaçın; ? ile yay ya da .expect kullan.

08 Eşzamanlılık zihniyeti

C/C++'ta eşzamanlılık "paylaş ve kilitle"dir; Rust'ta öncelik "sahiplik + mesajlaş"tır ve veri yarışları derleme zamanında imkânsızdır.

C/C++'ta çok thread'li kod yazmak, doğruluğu tamamen programcının disiplinine bırakan bir mayın tarlasıdır: hangi mutex hangi veriyi korur, kilit sırası nedir, bir alanı kilitsiz okumak güvenli mi? Bu soruların cevabı yorum satırlarında yaşar ve hata yaptığında veri yarışı (data race) ortaya çıkar — yeniden üretilmesi en zor bug sınıfı. Rust'ın iddiası radikaldir: güvenli kodda veri yarışı derleme zamanında imkânsızdır. Bu, ownership kurallarının ve iki işaretleyici trait'in (Send, Sync) doğal bir sonucudur.

SendTip başka bir thread'e güvenle taşınabilir (sahipliği geçebilir).
SyncTipe birden çok thread'ten aynı anda &T ile bakmak güvenlidir.
!Send / !SyncÖrn. Rc tek-thread'liktir; derleyici onu thread sınırından geçirmez.

Önce tercih edilen kalıp mesajlaşmadır: veriyi paylaşmak yerine sahipliğini bir kanal (channel) üzerinden gönderirsin. "Belleği paylaşarak iletişim kurma, iletişim kurarak belleği paylaş" felsefesi. Paylaşımlı durum gerçekten kaçınılmazsa Arc<Mutex<T>> kullanırsın — ama burada bile veriye mutex'i kilitlemeden erişemezsin, çünkü Mutex::lock() sana ancak kilitliyken geçerli bir &mut verir.

c++
// C++: mutex ve veri AYRI — kilitlemeyi unutmak derlenir
std::mutex m;
int sayac = 0;          // koruma sadece konvansiyon
sayac++;                // mutex'i unuttun: veri yarışı, derleyici sessiz
rust
// Rust: veri mutex'in İÇİNDE — kilitlemeden erişemezsin
use std::sync::{Arc, Mutex};
let sayac = Arc::new(Mutex::new(0));
{
    let mut g = sayac.lock().unwrap(); // kilit alındı
    *g += 1;                            // erişim ancak kilitliyken
} // g düşünce kilit otomatik bırakılır (Drop)

Farkı gör: C++'ta mutex ile koruduğu veri ayrı yaşar, ikisini birbirine bağlayan tek şey programcının niyetidir. Rust'ta veri mutex'in içine konur, dolayısıyla veriye erişmenin tek yolu kilidi almaktır. Üstelik kilit, kilit nesnesi (MutexGuard) kapsamdan çıkınca otomatik bırakılır — C++'taki "unutulan unlock" bug'ı yapısal olarak imkânsızdır.

C / C++RustNe zaman
std::threadstd::thread::spawnOS thread'i; benzer model.
std::mutex + veri (ayrı)Mutex<T> (veri içeride)Kilitsiz erişim derlenmez.
shared_ptr (atomik sayım)Arc<T>Thread'ler arası paylaşımlı sahiplik.
elle bırakılan kilitRAII guard (Drop)Unutulan unlock imkânsız.
concurrent queue + manuel senkron.mpsc kanalMesajlaşma; paylaşımdan kaçın.
NOT

Karar kuralı basit: veri akışı tek yönlüyse ya da iş parçaları arasında "sahiplik devri" mantıklıysa channel kullan; gerçekten paylaşımlı, her iki taraftan okunup yazılan bir durum varsa Arc<Mutex<T>> kullan. Rc'yi thread'ler arası kullanmaya çalışırsan derleyici !Send diye reddeder — bu bir engel değil, seni veri yarışından koruyan kontroldür. İlgili eşzamanlılık rehberi async ve thread havuzlarını da kapsar.

Bu bölümde

  • Öncelik "sahiplik + mesajlaş"; paylaşımlı kilit son çaredir.
  • Send/Sync sayesinde veri yarışları derleme zamanında engellenir.
  • Mutex<T> veriyi içine alır; kilitsiz erişim derlenmez, unlock unutulamaz.
  • Tek yönlü akış için channel, çift yönlü paylaşımlı durum için Arc<Mutex>.

09 Öğrenme yol haritası

Doğru sırayla öğrenirsen geçiş haftalar, yanlış sırayla aylar sürer; önce ownership'i içselleştir, kestirme kaçışlardan uzak dur ve küçük gerçek bir proje yaz.

Önerilen öğrenme sırası, her adımın bir öncekine yaslanacak şekilde tasarlandı. Bu sırayı atlamak — örneğin ownership'i tam oturtmadan trait'lere geçmek — tam olarak "borrow checker'la güreşme" dönemini uzatır.

ownership/borrow → enum/Option/match → Result/? → trait/generics →
koleksiyon/iterator → lifetimes → smart pointer (Box/Rc/Arc) → eşzamanlılık
SıraKonuNeden bu sırada
1Ownership & borrowingHer şeyin temeli; bunu atlarsan sonrası anlamsız.
2enum, Option, matchNULL'dan kurtuluş; pattern matching refleksi.
3Result & ?Hata yönetimi deyimini erken oturt.
4Trait & genericsKalıtım refleksini kompozisyona çevir.
5Koleksiyon & iteratorManuel döngü refleksini sök.
6LifetimesBorrow checker'ı tam anla; çoğu zaman örtük.
7Smart pointer (Box/Rc/Arc)Paylaşımlı sahipliği gerektiğinde.
8Eşzamanlılık & asyncEn son; tüm modeli kullanır.

Şimdi madalyonun diğer yüzü: erken kaçış tuzakları. Bunlar Rust'ı zorlaştıran değil, öğrenmeni geciktiren kestirmelerdir. Hepsi "borrow checker'a teslim olmamak için ödenen rüşvet"tir ve hepsi öğrenmeyi engeller.

DİKKAT

Üç büyük erken kaçış: (1) her şeyi .clone()'lamak — borrow checker'ı düşünmek yerine kopyayla satın almak; (2) Rc<RefCell<T>>'e erken sığınmak — bu paylaşımlı değiştirilebilirliği runtime'a iter ve aslında C++ alışkanlığını taklit eder, gerçekten gerekene kadar kullanma; (3) unsafe'e ya da .unwrap() spam'ına kaçmak — derleyiciyle güreşmek yerine onu devre dışı bırakmak. Bu üçü kısa vadede ilerletir, uzun vadede Rust'ı öğretmez.

Teori kadar pratik şart: küçük, gerçek bir proje yaz. "Hello world"den sonra en iyi öğretici, somut bir araç yapmaktır. Önerilen ilk projeler: bir komut satırı aracı (örn. grep benzeri bir arama, log filtreleyici, dosya istatistikçisi), basit bir HTTP istemcisi, ya da bir JSON/CSV dönüştürücü. Bu projeler ownership, Result, iterator ve string tiplerinin hepsine aynı anda dokunur — yani bu rehberin tamamını pratiğe döker.

The Book"The Rust Programming Language" — resmi, kapsamlı başlangıç (doc.rust-lang.org/book).
RustlingsKüçük, derlenmeyen alıştırmaları düzelterek öğrenme; refleks kazandırır.
std docsStandart kütüphane dokümantasyonu — örnekleri bol, günlük başvuru.
clippyKendi kodun üzerinde sürekli çalıştır; canlı deyim öğretmeni.
NOT

Kapanış: ilk haftalarda borrow checker'la geçirdiğin her saat, gelecekte üretimde oluşmayacak bir use-after-free, data race ya da null dereference'ın bedelidir — sadece zamanı öne çekiyorsun. Bir C/C++ programcısı olarak bu hataların maliyetini zaten biliyorsun; Rust onları derleyiciye fatura ediyor. Direnç geçicidir, sonra "bu nasıl derlendiyse doğrudur" rahatlığı kalıcıdır. Bu rehberdeki diğer Rust rehberleri her konuyu derinleştirir — sırayla üzerlerinden geç ve küçük bir proje yazmaya bugün başla.

Bu bölümde

  • Sıra: ownership → enum/Option → Result → trait → iterator → lifetimes → smart pointer → eşzamanlılık.
  • Erken kaçışlardan kaçın: her şeyi clone'lama, Rc<RefCell>/unsafe'e erken sığınma, .unwrap() spam'ı.
  • Küçük gerçek bir proje (CLI aracı, dönüştürücü) tüm kavramları aynı anda pekiştirir.
  • Kaynaklar: The Book, Rustlings, std docs, clippy; direnç geçicidir, garanti kalıcıdır.