Rust hangi problemi çözer?
Rust, bellek güvenliği ve veri yarışlarını compile-time kontrollerle önlemeyi hedefleyen, çöp toplayıcı gerektirmeyen sistem programlama dilidir. Bu güvenlik iddiası her program hatasının imkânsız olduğu anlamına gelmez. Yanlış algoritma, yetkisiz API, iş kuralı hatası, panik, deadlock, hatalı unsafe blok veya kaynak tüketimi yine mümkündür. Rust’ın ayırt edici özelliği, veri sahipliğini, ödünç almayı ve mutability kurallarını tür/derleme modeliyle ifade ederek bir hata sınıfını çalışma öncesinde yakalamasıdır.
Düşük gecikmeli servis, CLI, oyun motoru bileşeni, parser, WebAssembly modülü, embedded yazılım veya kaynak açısından kritik bir araçta uygun olabilir. Öğrenme eğrisi ve derleme süresi toplam teslim maliyetinin parçasıdır. Prototipte geniş bir bilimsel kütüphane gerekliyse Python; tarayıcı arayüzü için TypeScript; çok hızlı servis geliştirme ve sade dağıtım için Go daha verimli olabilir. Rust’ın güvenlik ve kontrol avantajı iş gereksiniminde değer üretiyorsa tercih güçlenir.
Rust ownership sistemi GC olmadan bellek ömrünü açık kılar. Fakat tasarım hedefi “clone mümkün olduğunca hiç olmasın” değildir. Gereksiz kopya pahalıysa ölçün; küçük `Copy` değerinde veya sahipliği sadeleştiren bilinçli `clone`da sorun yoktur. Zorunlu lifetime anotasyonu arttığında kodun veri sahipliği modeli karmaşık olabilir; önce struct’ın neye sahip olması gerektiğini ve referansın kim tarafından ne kadar süreyle kullanıldığını düşünün.
Cargo ile ilk proje ve güvenilir geliştirme döngüsü
`cargo new` ikili uygulama, `cargo new --lib` kütüphane iskeleti kurar. Cargo paketleri yönetir, derler ve test çalıştırır. `Cargo.toml` paket metadata’sı ve bağımlılıkları; `Cargo.lock` çözülmüş bağımlılık sürümlerini içerir. Uygulama paketlerinde lock dosyasını sürüm kontrolünde tutmak tekrar üretilebilir build’e yardım eder. Kütüphane bakımında lock kullanımı geliştirme/test için yararlı olsa da downstream kullanıcı bağımlılıklarını library’nin kilidi belirlemez; Cargo Book yönergelerini takip edin.
`cargo check` kodu hızlı tip/borrow kontrolünden geçirir, tam binary link etmeden hata bulur. `cargo test` birim, integration ve documentation testlerini derleyip çalıştırır. `cargo fmt --check` biçimi doğrular, `cargo clippy` yaygın hatalı veya gereksiz örüntüleri gösterir. Komutlar, CI’da cargo ve rustc sürümü belli biçimde tekrar edilmelidir. Bir Clippy uyarısının yalnızca biçim önerisi mi yoksa correctness uyarısı mı olduğunu anlayın; `allow` ekleyerek tüm lintleri susturmak güvenilir kapı değildir.
Rust toolchain’i rustup ile yönetmek yaygındır. Bir projede MSRV (minimum desteklenen Rust sürümü), edition ve platform hedefini açıkça belirtin. Bir bağımlılığın yeni sürümü eski compiler ile derlenmeyebilir. `cargo update` lock dosyasını değiştirir; değişikliği gözden geçirip test edin. Bağımlılık seçerken crates.io paketinin bakım, lisans, unsafe kullanımı, transitive dependency ve güvenlik duyurularını araştırın. Küçük bir proje için tek bir yardımcı fonksiyon adına onlarca bağımlılık eklemek bakım maliyetidir.
Sahiplik ve ödünç alma: temel sezgi
Rust’ta her değer belirli bir anda bir sahibin kapsamındadır; sahibi kapsamdan çıktığında değer bırakılır. Bir non-Copy değeri başka değişkene atamak çoğu durumda sahipliği taşır (move), eski bağlayıcı artık geçerli olmaz. Fonksiyona değer geçirmek de türün kurallarına göre move veya copy yapar; fonksiyon bittiğinde yerel sahibi bırakılabilir. Bu kurallar allocation, file handle ve diğer kaynakların ne zaman bırakılacağını görünür kılar. C/C++’taki elle `free` çağrısına ihtiyaç duymamak için değer ömrü compile-time denetlenir.
Bir fonksiyonun sadece okumaya ihtiyacı varsa `&T` referansı, değiştirmesi gerekiyorsa `&mut T` alabilir. Aynı anda birçok immutable referans veya tek mutable referans kuralı, aliasing ve data race risklerini düşürür. Borrow checker hatası ilk etapta iş akışını yavaşlatabilir; mesajın hangi verinin ne kadar yaşaması gerektiğini sorguladığını çözün. Genellikle çözüm lifetime sözdizimini ezberlemekten önce veriyi gereksiz paylaşmayı bırakmak, sahipliği taşıyıp döndürmek veya işi daha küçük fonksiyonlara bölmektir.
String ve `&str` farkı öğretici bir örnektir: `String` heap üzerinde sahip olunan, büyütülebilen metni tutar; `&str` mevcut UTF-8 metne ödünç alınmış görünümü tutar. `String`’i fonksiyona move etmek istemiyorsanız `&str` parametresi kabul etmek çoğu okuma fonksiyonu için uygundur. Çıktı olarak yeni ve bağımsız metin üretilecekse `String` döndürün. Referans döndürürken kaynağın ömrü sonuçtan uzun olmalıdır; aksi takdirde derleyici geçersiz referansı reddeder.
01fn greeting(name: &str) -> String {02 format!("Merhaba, {name}!")03}04 05fn main() {06 let name = String::from("Yunus");07 let message = greeting(&name);08 println!("{message}");09 println!("{name}");10}Enum, pattern matching ve `Option` ile geçersiz durumu azaltmak
Rust enum’ları yalnızca sayısal sabit listesi değildir; her varyant farklı veri taşıyabilir. `Option<T>`, bir değer ya vardır ya yoktur der; null benzeri değeri sessizce her tipe eklemek yerine çağıranı yokluk durumunu ele almaya yönlendirir. `Result<T, E>` başarı değerini ve hata değerini taşır. `match`, varyantların tümünü ele almayı denetler. `if let` veya `let else` tek durumun önemli olduğu yerde daha sade yazım sağlar. Bu tipler ile hata yolu kodun görünür parçasıdır.
Bir API yanıtını Rust struct’a deserialize etmek, gelen JSON’un iş kuralını sağladığını tek başına göstermez. Serde verinin sözdizimi/şekil dönüşümünü sağlar; aralık, yetki, sahiplik ve domain doğrulaması ayrıca yazılmalıdır. Bir yapılandırma alanı zorunlu mu, eksikse varsayılan mı, bilinmeyen alan reddedilsin mi gibi kararlar sözleşmeye göre verilmelidir. `unwrap()` geliştirme sırasında hızlı prototip olabilir, fakat güvenilmeyen veri veya I/O hatasında paniğe dönüşebilir.
İş kuralı kodunda `Result` döndürüp hatayı üst katmanda kullanıcı/API/log ihtiyacına göre ele almak çoğunlukla daha kontrollüdür. `?` operatörü hata türü çağırana dönüştürülebiliyorsa erken döndürür. Hata türünü `thiserror` gibi kütüphanelerle tanımlamak domain hatalarını anlaşılır kılar; `anyhow` uygulamanın üst sınırında bağlam eklemek için pratik olabilir. İkisini ihtiyaç olmadan eklemeyin. Kütüphane kullanıcısına hata türü public sözleşmedir, bu nedenle değişiklik uyumluluğunu düşünün.
Borrow checker, yalnızca geçersiz referansı değil; belirsiz veri sahipliği kararlarını da görünür eder.
Lifetime: referansın kaynağını ifade etmek
Lifetime anotasyonu referansın ömrünü uzatmaz veya belleği “yaşatmaz”. Compiler’a giriş ve çıkış referansları arasındaki ilişkiyi anlatır. Örneğin iki string’ten daha uzun olanı döndüren fonksiyon, sonucun hangi girdiye referans verdiğini gösteren bir lifetime ilişkisine ihtiyaç duyabilir. Elision kuralları basit fonksiyonlarda anotasyonu kaldırır; her yerde `'static` yazmak gerçek veri ömrünü değiştirmez. `'static` gerçekten program boyunca süren veri veya uygun literal için kullanılır.
Lifetime hatası alanında veri kaynağının sahibi fonksiyon sonuna kadar yaşıyor mu, sonuç gerçekten ödünç referans mı olmalı, yoksa `String`/`Vec` gibi sahipli değer döndürmek daha basit mi diye bakın. Struct referans tutuyorsa, referansın bağlı olduğu dış veri ömrü struct kullanımını kısıtlar. Uzun ömürlü cache veya callback’te bu model API’yi karmaşıklaştırabilir. Ownership modeli değiştirilecekse bunu sonradan lifetime eklemekten önce yapın.
Tüm programı lifetime’sız yapmak hedef değildir; gereksinim yokken lifetime anotasyonuyla API’yi ağırlaştırmamak hedeftir. Çoğu uygulama veriyi sahipli biçimde taşır, ödünç almayı kısa fonksiyon çağrılarında kullanır. Iterator, parser ve zero-copy kütüphanesi gibi performans sınırı olan alanlarda lifetime ilişkisi bilinçli fayda sağlar. Önce anlaşılır doğru kodu yazın, profil darboğaz gösterdiğinde kopyalama/allocation davranışını optimize edin.
Concurrency: `Send`, `Sync` ve veri yarışı güvenliği
Rust tip sistemi thread’ler arasında taşınabilir veya paylaşılabilir veriye `Send` ve `Sync` trait’leri üzerinden bazı güvenlik koşulları koyar. Bunlar her paralel programın race-free sonucunu garanti eden üst kavramlar değildir; unsafe kod, mantıksal yarış ve deadlock hâlâ mümkün olabilir. Standart `Arc<Mutex<T>>`, paylaşılan ve korunan mutable veri için kullanılabilir. Thread’ler arasında mesaj geçirme ise ownership aktarımıyla durum paylaşımını azaltabilir. Seçim veri akışı ve performans gereksinimine bağlıdır.
Asenkron Rust, `async` fonksiyonlarının `Future` üretmesine dayanır; gelecek tek başına çalışmaya başlamaz, bir executor tarafından poll edilir. Tokio gibi çalışma zamanları ağ, timer ve task planlama sağlar. Projenin her katmanında async kullanmak gerekmez. Async sınırında bloklayan dosya veya CPU işi executor thread’lerini tutabilir; uygun blocking pool veya ayrı iş kuyruğu kullanılmalıdır. Kullandığınız runtime’ın güncel ve özellikleri açık seçilmiş sürüm belgesini izleyin.
Birçok küçük task yaratmak sınırsız kaynak yaratma gerekçesi değildir. İstek kabulü, concurrency limiti, backpressure, cancellation ve graceful shutdown politikaları tanımlanmalıdır. `select!` ile birden çok gelecek arasından beklemek, timeout ve kapanış sinyalini birlikte ele alabilir; iptal edildiğinde alt servisin gerçekten iptal olup olmadığını kontrol edin. Detached task hatası kullanıcıya ulaşmayabilir. JoinHandle’ı izleyin ve servis kapanırken bekleyen işleri yönetilebilir biçimde tamamlayın.
Test, dokümantasyon ve unsafe sınırı
`cargo test` birim, entegrasyon ve doctest’leri çalıştırır. Unit test aynı modülün iç detaylarına erişebilir; integration testi kütüphaneyi kullanıcı gibi çağırarak public API’yi sınar. Doc comment içindeki kod örneği de doctest olabilir; belgelenmiş kullanımın derlenmesi güven sağlar. Testleri küçük tutup dış ağ ve saat gibi değişkenleri kontrol edin. Property-based test veya fuzzing parser ve binary formatlar için faydalıdır; ancak beklenen property’leri doğru tanımlama sorumluluğu sürer.
`unsafe` Rust, compiler’ın güvenlik garantisi sağlayamadığı ve program yazarının belirli invariant’ları üstlendiği sınırdır. Her unsafe blokta hangi koşulun sağlandığını belgeleyin, mümkün olan en küçük alana kapatın ve güvenli public API arkasına alın. Bir crate’in unsafe kodu az olması kalite göstergesi olabilir ama tek başına doğruluk ispatı değildir. Miri gibi araçlar bazı undefined behavior senaryolarını bulabilir, fakat tüm çalışma davranışını kapsamaz. Bağımlılığın unsafe yüzeyini ve bakım düzeyini denetleyin.
Clippy ve formatter, testin yerini tutmaz. `cargo audit` gibi araçlar bilinen advisory’leri bulmaya yardımcı olabilir; dependency tree’yi, lock değişimini ve lisansları da gözden geçirin. CI’da format, lint, test, build ve hedef platform kontrolleri sıralı çalışsın. Release profile optimizasyonları build süresini ve hata ayıklamayı etkiler; profil ayarını ölçmeden değiştirmeyin. Paniklerin production sınırında nasıl ele alınacağını—işi durdurma, task kurtarma veya servis restart’ı—belirleyin.
Örnek proje: güvenli ve hızlı log arama CLI’sı
Rust öğrenmek için iyi bir proje, büyük log dosyasında tarih aralığı ve ifade filtresi uygulayan CLI olabilir. Dosya I/O ve satır ayrıştırma standart kütüphaneyle başlar; argüman parsing için `clap`, seri hale getirme için `serde` eklenebilir. Gerekli feature’ları seçin, binary’nin boyutunu ölçün. Parser geçersiz timestamp ve UTF-8 sorunlarını satır numarasıyla raporlasın; tüm dosyayı belleğe almaktansa stream ederek RAM’i sınırlandırın. Sıkıştırılmış dosya ve regex desteğini gerçek ihtiyaç doğduğunda ekleyin.
Tasarımdaki owned/borrowed kararını ölçün: her satırı String’e kopyalamak basit; fakat büyük dosyada allocation yükü olabilir. Önce kolay doğrulanır sürüm, sonra Criterion gibi benchmark aracıyla temsili dosyada test. Çıktıyı pipe üzerinden başka programa verme, `--json` modu, exit code ve bozuk satır politikası CLI kullanıcıları için önemlidir. Log içeriğinde token ya da kişisel veri olabilir; varsayılan olarak tüm satırı hata telemetrisine göndermeyin.
Alternatif projeler: dosya checksum doğrulayıcı, küçük HTTP health probe, config validator, WebAssembly’de çalışan markdown parser, embedded sensör okuyucu, e-posta kuyruğu worker’ı. Ağ uygulamasında Tokio kullanılıyorsa cancellation ve task sınırını inceleyin. GUI arayüzü Rust ile yapılabilir, ancak UI framework ekosisteminin olgunluğunu ve hedef platform kapsamını kontrol edin. Portfolyoya bir completed demo, test, benchmarking methodology, `unsafe` rationale ve açık sınırlamalar eklemek gösterişli ama yarım framework’ten daha değerlidir.

Yaygın başlangıç hataları ve iş çözümleme örneği
İlk hata, borrow checker’ı susturmak için her değerde `clone()` çağırmaktır; program derlenir ama gereksiz allocation yapabilir. İkinci hata, bütün hata durumlarında `unwrap()` kullanmaktır; bozuk kullanıcı girdisi programı sonlandırabilir. Üçüncü hata, her nesne için `Arc<Mutex<T>>` eklemektir; gereksiz ortak mutable durum, locking ve deadlock riski yaratır. Dördüncü hata, tutarsız `unsafe` veya lifetime hilesiyle compiler’ın modelini aşmaya çalışmak. Beşinci hata, 100 dependency ile “production-ready” görünmek.
Örnek iş: e-ticaret sepetinden sipariş özeti üretin. Para birimi küçük birim integer veya decimal kütüphanesiyle temsil edilir; fiyat yuvarlama politikası açık yazılır. Sepet öğeleri `Vec<Item>` sahipli yapıdadır; hesap fonksiyonu `&[Item]` ile okuyabilir. Geçersiz miktar `Result` hatası, promosyon uygunluğu ayrı saf fonksiyon, stok doğrulaması ise servis sınırında yapılır. DB transaction ve ödeme sağlayıcısı yan etkisi hesap fonksiyonundan ayrı tutulur. Böylece ownership sadeleşirken business rule test edilebilir.
Ödeme işlemini async executor içinde sınırsız başlatmaz, sağlayıcı timeout’u belirler ve idempotency anahtarı kullanırsınız. DB başarısızlığı/ödeme başarısı ikilisi dağıtık transaction sorusunu doğurur; outbox/saga gibi daha büyük tasarımlar gerçek tutarlılık ihtiyacı varsa ele alınır. Birkaç borrowed referans yazmak bu sistem sorununu çözmez. Rust memory safety ile bazı bellek hatalarını önleyebilir; ürün ve dağıtık sistem kararlarını hâlâ mühendis belirler.
Geliştirirken compiler mesajını küçük bir deneyle anlamak faydalıdır: hangi satır referansı kullanıyor, hangi scope sona eriyor, değer Copy mi Move mu? Rust Book sahiplik, borrowing, struct, enum, error ve test konularını sıralı anlatır. Daha ileri ayrıntı için Rust Reference, Cargo Book ve standard library belgelerine geçin. Crate belgeleri tam kullandığınız sürümle eşleşmeli. Eski tutorial’da deprecated API varsa neden değiştiğini release notes’tan araştırın.
Proje seçimi ve üretime çıkış kontrol listesi
Rust seçerken şunları sorun: gecikme veya bellek hedefi ölçülmüş mü? Runtime’da GC olmaması operasyonel bir gereksinim mi? FFI, WASM veya embedded hedef var mı? Ekip borrowing modelini öğrenmeye yatırım yapabilir mi? Gerekli kütüphaneler olgun ve hedef platformu destekliyor mu? Build ve cross compilation süreleri CI bütçesine uyuyor mu? Bu sorulara “evet” yanıtı birkaç sınırda toplanıyorsa Rust modülünü mevcut uygulamaya eklemek, tüm sistemi yeniden yazmaktan daha iyi olabilir.
Yeni servisin üretim kontrolü: MSRV belirlenmiş, config/secret güvenli, hata ve panic politikası açıklanmış, timeout/limit tanımlanmış, logging kişisel veriyi koruyor, graceful shutdown test edilmiş, lock/dependency gözden geçirilmiş, format/lint/test/CI geçiyor, binary hedef platformda denenmiş. Bellek güvenliği bu kontrollerin yerine geçmez. Container içindeki kullanıcı, filesystem ve network yetkilerini de en düşük seviyede tutun.
Kaynak sırası: resmi Rust Book temeller ve ownership için; Rust Reference dilin kesin özellikleri için; Cargo Book paket/build/test davranışı için; standard library docs API ayrıntısı için; Async Book ve runtime belgeleri async tasarım için. Topluluk yazıları örnek ve sezgi sağlar, fakat unsafe, soundness ve güncel API iddiaları birincil belgelerle doğrulanmalıdır. Kodu farklı sürümde kopyalamadan önce `cargo check` ve test çalıştırın.
Sonuç: daha güvenli kod, daha iyi tasarım kararıyla başlar
Rust derleyicisinin katılığı bazen üretkenlik engeli gibi hissedilir; çoğu zaman kaynak ve referans ömrünü açık hâle getirir. İlk kodda basit sahipli değerleri tercih etmek, borçlanmayı okuma sınırlarında kullanmak, hatayı `Result` ile görünür kılmak ve küçük birim test yazmak öğrenme sürecini hızlandırır. Lifetime ve trait sistemini ancak ihtiyacınız ortaya çıktıkça derinleştirin. Her uyarıyı düşünmeden susturmak kısa vadeli rahatlık, uzun vadeli belirsizlik yaratır.
Rust her ürünün varsayılan dili değildir. Gerçek performans, güvenlik, çalışma zamanı ve dağıtım gereksinimine göre tercih edilir. Doğru iş sınırında kullanıldığında bellek hatalarını azaltır, kaynak kullanımını görünür kılar ve bakım yapan ekibe sözleşme sunar. Sistemin yanlış gereksinimini kusursuz derlemek hâlâ yanlış sistem üretir; domain testi ve operasyon geri bildirimi vazgeçilmezdir.
İlk haftalık hedef olarak ownership temelleri, küçük CLI, hatalı girdiler, `cargo test` ve profiling ile bir mini ürün tamamlayın. Ardından kodu başka bir geliştiriciye kurdurup README’nin yeterli olup olmadığını görün. İyi öğrenme projesi yalnızca borrow checker’dan geçmez; kullanıcı girdisini güvenli işler, beklenen sonucu verir, hatayı anlaşılır açıklar ve nasıl yeniden üretileceğini belgeler.
Resmî kaynaklar ve ileri okuma
Teknik davranışları birincil kaynaklardan kontrol etmek ve konuyu uygulayarak derinleştirmek için:
- The Rust Programming Language (The Book)
- Rust Reference
- The Cargo Book: cargo test
- Rust standard library
- Rust Async Book
- Rustonomicon: Unsafe Rust
Kaynaklar ilgili dil, standart kütüphane veya aracın birincil dokümantasyonudur. Sürüme göre değişebilen ayrıntılarda kendi projenizin kullandığı sürümün belgelerini esas alın.