1 puan yazan GN⁺ 2026-05-06 | 2 yorum | WhatsApp'ta paylaş
  • Async Rust, executor’dan bağımsız kodun sunucularda ve mikrodenetleyicilerde birlikte çalışmasını mümkün kılıyor; ancak derleyicinin ürettiği durum makinesi nedeniyle özellikle gömülü tarafta ikili dosya boyutundaki artış belirgin oluyor
  • bar() gibi iki adet await noktası olan basit bir örnek bile 360 satırlık MIR ile Unresumed, Returned, Panicked, Suspend0, Suspend1 durumlarını üretiyor; eşzamanlı sürüm ise yalnızca 23 satır gerektiriyor
  • Tamamlanmış bir future yeniden poll edildiğinde panic yerine Poll::Pending döndürecek şekilde değiştirmek, unsafe davranış olmadan sözleşmeyi karşılamayı mümkün kılıyor ve deneylerde gömülü firmware’in ikili dosya boyutunu %2 ila %5 azaltıyor
  • Await içermeyen async { 5 } bile şu anda varsayılan üç durumlu bir durum makinesi üretiyor; ancak her seferinde Poll::Ready(5) döndürecek şekilde optimize edilirse gömülü ikili boyutu %0,2 azalıyor
  • Önerilen Project Goal, sürüm modunda tamamlanma sonrası panic’in kaldırılması, await içermeyen async block’larda durum makinesinin kaldırılması, tek await’li future’ın inline edilmesi ve aynı durumların derleyicide birleştirilmesini hedefliyor

Async Rust’ta derleyici seviyesinde şişme sorunu

  • Async Rust, executor’dan bağımsız kodun sunucularda ve mikrodenetleyicilerde aynı anda çalışmasını sağlıyor; ancak küçük mikrodenetleyicilerde ikili dosya boyutundaki artış özellikle dikkat çekiyor
  • Rust blogu async/await’i sıfır maliyetli soyutlama olarak tanıttı; ancak async gerçekte ciddi miktarda şişme yaratıyor ve masaüstü ile sunucu tarafında da aynı sorun var, sadece bellek ve işlem kaynakları fazla olduğu için daha az görünür kalıyor
  • Async kod yazarken şişmeyi önlemeye yönelik geçici çözümlerin ardından, sorunu derleyici tarafında çözmek için bir Project Goal sunuldu
  • Future’ın gerekenden fazla büyümesi ve çok sayıda kopyalama yapılması kapsam dışında bırakıldı

Üretilen future’ın yapısı

  • Örnek kodda foo() async { 5 } döndürüyor ve bar() ise foo().await + foo().await çalıştırıyor
  • bar içinde iki await noktası bulunduğundan durum makinesinde en az iki durum gerekse de, gerçekte daha fazla durum üretiliyor
  • Rust derleyicisi çeşitli geçişlerde MIR dökümü yapabiliyor ve coroutine_resume geçişi son async’e özgü MIR geçişi
    • Async, LLVM IR’de artık yer almasa da MIR’de kalıyor; dolayısıyla async’in durum makinesine dönüştürülme süreci MIR geçişlerinde gerçekleşiyor
  • bar fonksiyonu 360 satırlık MIR üretiyor; eşzamanlı sürüm ise yalnızca 23 satır kullanıyor
  • Derleyicinin çıktıladığı CoroutineLayout, fiilen enum biçimindeki bir durum kümesi
    • Unresumed: başlangıç durumu
    • Returned: tamamlanmış durum
    • Panicked: panic sonrası durum
    • Suspend0: ilk await noktası ve foo future’ını saklıyor
    • Suspend1: ikinci await noktası ve ilk sonucu ile ikinci foo future’ını saklıyor
  • Future::poll güvenli bir fonksiyon olduğundan, future zaten tamamlandıktan sonra tekrar çağrılsa bile UB’ye yol açmamalı
    • Şu anda Suspend1 sonrasında Ready döndürüp future’ı Returned durumuna geçiriyor
    • Bu durumda yeniden poll edilirse panic oluşuyor
  • Panicked durumu, async fonksiyon panic verdikten sonra bu panic catch_unwind ile yakalandığında ilgili future’ın tekrar poll edilmesini engellemek için var gibi görünüyor
    • Panic sonrasında future eksik bir durumda kalabilir; bu yüzden yeniden poll etmek UB’ye yol açabilir
    • Bu mekanizma mutex poisoning’e oldukça benziyor
    • Panicked durumuna ilişkin bu yorum için kesin bir belge bulmak zor; bu yüzden buna dair güven düzeyi yaklaşık %90

Tamamlandıktan sonra poll edildiğinde gerçekten panic gerekli mi?

  • Returned durumundaki future şu anda panic veriyor, ancak bunun zorunlu olması gerekmiyor
    • Gerekli olan tek koşul, UB’ye yol açmaması
  • Panic görece maliyetli ve optimizasyonla kaldırılması zor olan yan etkili bir yol ekliyor
  • Tamamlanmış future yeniden poll edildiğinde Poll::Pending döndürmek, unsafe davranış olmadan Future türünün sözleşmesini karşılayabiliyor
  • Derleyici bu yaklaşımla değiştirilip deney yapıldığında, async gömülü firmware’de ikili dosya boyutunda %2 ila %5 azalma görüldü
  • Bu davranışın, tıpkı tamsayı taşmasındaki overflow-checks = false gibi bir anahtarla sunulması öneriliyor
    • Hatalı davranışı hemen görünür kılmak için debug derlemelerinde panic devam ediyor
    • Release derlemelerinde ise daha küçük future’lar elde edilebiliyor
  • panic=abort kullanıldığında Panicked durumunun kendisinin tamamen kaldırılması mümkün olabilir; bunun etkisi ayrıca değerlendirilmeli

Await olmasa bile her zaman durum makinesi üretiliyor

  • foo() yalnızca async { 5 } döndürdüğünden, elle yazılmış en iyi uygulama durum içermeyen ve her zaman Poll::Ready(5) döndüren bir future olurdu
  • Ancak derleyicinin ürettiği MIR’de yine de Unresumed, Returned, Panicked olmak üzere temel üç durum bulunuyor
    • Poll sırasında mevcut durumun discriminant’ı kontrol edilip dallanılıyor
    • Tamamlandıktan sonra yeniden poll edilirse `async fn` resumed after completion assert’i ile panic veriliyor
  • Bu durumda durum makinesi üretmek yerine her seferinde Poll::Ready(5) döndürecek şekilde optimize etmek mümkün
  • Bu değişiklik derleyiciye deneysel olarak uygulandığında gömülü ikili boyutu %0,2 azaldı
    • Kazanç büyük değil, ancak basit bir optimizasyon olduğu için uygulanmaya değer olabilir
  • Bu optimizasyon davranışı bir miktar değiştiriyor, ancak etkilenecek olanlar yalnızca sözleşmeye uymayan executor’lar
    • Mevcut derleyici sonraki poll işleminde panic veriyor
    • Optimizasyondan sonra ise future her zaman Ready döndürüyor

Yalnızca LLVM yeterli değil

  • MIR çıktısı verimsiz olsa bile LLVM’nin her şeyi temizleyebildiği durumlar var, ancak koşullar sınırlı
    • Future yeterince basit olmalı
    • opt-level=3 kullanılmalı
  • Future karmaşıklaştıkça LLVM bunu kaldıramıyor; deyimsel async Rust kodunda future’lar derin biçimde iç içe geçtiği için karmaşıklık hızla büyüyor
  • Gömülü sistemler veya wasm gibi boyut optimizasyonunun sık yapıldığı ortamlarda LLVM her şeyi optimize edemiyor
  • Godbolt örneği: https://godbolt.org/z/58ahb3nne
    • Üretilen assembly’de LLVM, foo’nun 5 döndürdüğünü biliyor ama bar sonucunu 10’a optimize edemiyor
    • foo için poll fonksiyonu çağrısı da hâlâ duruyor
    • Bunun nedeni, derleyicinin tamamen çözemediği potansiyel panic yolları
    • LLVM, foo’nun pratikte yalnızca bir kez çağrıldığını ve panic vermediğini bilmiyor
  • IR içindeki panic dalları yorum satırına alındığında optimizasyon daha iyi oluyor: https://godbolt.org/z/38KqjsY8E
  • LLVM’den sonradan optimizasyon beklemek yerine, derleyicinin LLVM’ye daha iyi girdi vermesi gerekiyor

Future inline etme iyi çalışmıyor

  • Inline etme, sonrasındaki optimizasyon geçişlerini mümkün kıldığı için önemli; ancak üretilen Rust future’ları şu anda erken aşamada inline edilmiyor
  • Her future kendi implementasyonunu aldıktan sonra LLVM ve linker inline etme fırsatı yakalıyor, ancak önceki sorunlar nedeniyle bu aşama artık çok geç kalıyor
  • En doğrudan inline fırsatı, bar() fonksiyonunun yalnızca foo(blah).await yapması durumu
    • Trait kullanılarak soyutlama kurulurken sık görülen bir desen
    • Mevcut derleyici bar için bir durum makinesi oluşturuyor ve onun içinde foo durum makinesini çağırıyor
    • Daha verimli yaklaşımda bar, doğrudan foo future’ı olabilir
  • Preamble ve postamble olduğunda durum daha karmaşık
    • Örneğin bar(input), input > 10 ile blah oluşturuyor, ardından foo(blah).await yapıyor ve sonuca * 2 uyguluyor
    • Bu, özellikle trait implementasyonlarında async fonksiyonları farklı imzalara dönüştürürken sık görülüyor
  • Bu tür bir bar da kendi async durumuna ihtiyaç duymuyor
    • Tek await noktasını aşarak korunması gereken veri, foo içinde yakalanan değerler dışında yok
    • Yine de bar doğrudan foo’nun kendisi olamaz; fakat durumun büyük bölümü fooya bırakılabilir
  • Elle yazılmış bir uygulamada BarFut, Unresumed { input } ve Inlined { foo: FooFut } durumlarına sahip olabilir
    • İlk poll’da preamble çalıştırılır, foo(blah) oluşturulur ve Inlined durumuna geçilir
    • Sonrasında foo.poll(cx) sonucuna postamble uygulanır
  • İlk await noktasından önce kodu önceden çalıştırmak mümkün olsaydı Unresumed durumu da kaldırılabilirdi; ancak future’ın poll edilmeden önce hiçbir şey yapmaması garanti edildiği için bu değiştirilemez
  • Poll edilmekte olan bir future’ın özellikleri sorgulanabilse ek inline optimizasyonları mümkün olabilir
    • Örneğin future’ın ilk poll’da her zaman ready döndürdüğü bilinseydi, çağıran future içinde o await noktası için durum oluşturmaya gerek kalmazdı
    • Bu tür optimizasyonlar özyinelemeli biçimde uygulanırsa birçok future çok daha basit durum makinelerine indirgenebilir
  • Mevcut rustc yapısında her async block ayrı ayrı dönüştürülüyor ve sonrasında ilgili veriler korunmadığı için bu tür sorgular mümkün görünmüyor
  • Future inline etme henüz deneysel olarak uygulanmadı, ancak ikili boyut ve performans açısından büyük fayda sağlaması bekleniyor

Aynı durumların birleştirilmesi

  • Async block içindeki her await noktası, durum makinesine ek bir durum ekliyor
  • Aşağıdaki gibi bir kod doğal görünse de iki dalda da aynı async fonksiyon await edildiği için iki özdeş durum oluşuyor
    • CommandId::A => send_response(123).await
    • CommandId::B => send_response(456).await
  • Bu durumda CoroutineLayout içinde send_response için aynı coroutine türünü tutan _s0, _s1 alanları ayrı ayrı oluşuyor ve Suspend0, Suspend1 adlı iki durum yaratılıyor
  • Bu fonksiyonun MIR’i 456 satır uzunluğunda ve birçok temel blok fiilen yinelenmiş durumda
  • Kod önce yalnızca yanıt değerini hesaplayıp sonra tek kez send_response(response).await yapacak şekilde elle yeniden düzenlenirse yinelenen durumlar ortadan kalkıyor
    • CommandId::A için 123
    • CommandId::B için 456
    • Sonrasında send_response(response).await
  • Yeniden düzenleme sonrasında CoroutineLayout içinde depolanan tek bir future kalıyor ve yalnızca bir Suspend0 durumu bulunuyor
  • Toplam MIR uzunluğu 302 satıra düşüyor ve tekrar ortadan kalkıyor
  • Bu nedenle aynı kod yollarını ve durumları bulup tekilleştiren bir optimizasyon geçişi faydalı görünüyor
    • Bu optimizasyonun future inline etme geçişiyle iyi birleşmesi muhtemel

Deney bağlantıları ve ek benchmark’lar

Project Goal için destek çağrısı

  • Bu çalışma, derleyici tarafında ilerletilmek üzere bir Project Goal olarak sunuldu: https://rust-lang.github.io/rust-project-goals/2026/async-statemachine-optimisation.html
  • Finansman olmadan çok fazla ilerleme kaydetmek zor olduğu için, bu çalışmadan fayda sağlayacak şirket veya kuruluşların kısmi ya da tam desteğine ihtiyaç var
  • İletişim adresi dion@tweedegolf.com
  • İş kapsamı ve gerekli finansman miktarı esnek, ancak €30k ile işin tamamının ya da önemli bir bölümünün bitirilebileceği tahmin ediliyor

2 yorum

 
GN⁺ 2026-05-06
Hacker News görüşleri
  • Başlığın biraz abartılı olduğuna katılıyorum, ama metin iyi yazılmış ve ana fikir de iyi aktarılmış
    Rust async konusunda güçlü bir görüş belirtecek kadar çok deneyimim yok ama birkaç şey gözüme çarptı
    İyi tarafı, açık bir runtime kullanabilmeniz. Projenin tamamını async ile kirletmek yerine, varsayılanı senkron tutup runtime’ı yalnızca G/Ç “sınırlarında” kullanabiliyorsunuz
    Üzerinde çalıştığım projeye bu yaklaşım iyi uydu ve Zig’in G/Ç kodunda izlediği stratejiye de epey benziyor gibi görünüyor. Bu durumda function coloring sorunu da büyük ölçüde çözülmüştü; ayrıca G/Ç ile CPU ağırlıklı kodu sıkı biçimde ayırmamız gerektiği için açık G/Ç runtime’ı doğal geldi
    Kötü tarafıysa tüm ekosistemin tokio’ya fazla bağımlı görünmesi. Bu, Java’da GC’nin teoride isteğe bağlı olup pratikte herkesin aynı üçüncü taraf GC runtime’ını kullanmasına ve hangi kütüphaneyi alırsanız alın o runtime’ın dayatılmasına benziyor. Böyle merkezi bir bağımlılık sağlıklı değil

    • Bağlama göre tüm ekosistem tokio’ya bağımlı gibi görünebilir, ama gömülü Rust tarafına bakınca bu daha anlaşılır geliyor
      İş istasyonu işlemcilerindeki async runtime gereksinimleri ile RP2040 gibi ortamlardaki gereksinimler çok farklı. Buna rağmen backend değiştirilebildiği için, küçük ARM M0 mikrodenetleyiciler için async G/Ç kodu yazarken embedded odaklı embassy runtime’ını kullanırsanız kod başka ortamlarda yazdığınızla neredeyse aynı görünüyor
      Aynı trait’leri ve arayüzleri kullandığınız için runtime ayrıntıları hakkında daha az düşünmeniz gerekiyor. Küçük bir RTOS kullanmakla ya da async ortamını kendiniz kurmakla kıyaslayınca oldukça iyi
      embassy’de async kod yazarak öğrendiklerinizi başka alanlara da taşıyabiliyorsunuz
    • Alternatifin ne olduğunu merak ediyorum. tokio kullanmaktan memnunum ama başkalarının smol, async-std, glommio gibi başka yürütücüler kullanması da iyi
      tokio standart kütüphanenin parçası olmasa da iyi bakılıyor, bu yüzden mevcut durum bana makul görünüyor. Hatta standart kütüphaneye girerse başka yürütücüler kullanmak zorlaşır ve standart kütüphaneyi başka platformlara taşımak da daha güç hale gelir diye endişeleniyorum
      Tabii bu kaygı temelsiz de olabilir
    • Java’dan bahsetmeniz ilginç, çünkü Java da tarihsel olarak benzer sorunlar yaşadı
      Loglama bugün büyük ölçüde slf4j etrafında toparlandı ama hâlâ başka şeyler kullanan kütüphaneler var; ortak yardımcı araçlar önce Apache Commons’tı, şimdi ise çoğu yerde Guava kullanılıyor
      JSON tarafı kısmen Jackson’da birleşti ama Gson ve Simple-json da yaygın; null izinliliği anotasyonları ise resmileşmeyen JSR-305’in gayriresmî dağıtımlarından checker framework’e, oradan da son dönemde JSpecify’a kayıyor
      Bu tür temel parçaları dilin sağlaması gerekir; aksi halde parçalanma ve fiilî standart kütüphanelerin çoğalması kaçınılmaz olur
    • async kullanıp da tokio’ya bağımlı olmadan Rust’tan yararlanabileceğiniz çok alan var. Aslında tamamen tokio’ya bağlanmış görünen yer daha çok web/sunucu tarafı
      Kütüphaneleri yürütücüden bağımsız yazmak çok zor değil ama sürekli dikkat gerektiriyor ve topluluğun büyük kısmında bunun her zaman korunduğu söylenemez
  • Harika bir yazı. Bu tür optimizasyon derinlemesine analizlerini seviyorum ve proje hedeflerinin de başarıya ulaşmasını umuyorum
    Derleyicinin “önemsiz” görünen durumları optimize etmeye çoğu zaman fazla emek harcamadığını düşündüğüm oldu
    Yine de başlık, içeriğe göre fazla dramatik. “Async Rust Optimizations the Compiler Still Misses” olsaydı da tıklardım

    • Başlığı sadece doğru olduğu için böyle seçtim. async 2019 civarında geldiğinden beri aslında çok şey değişmedi
      Artık trait’lerde ve closure’larda async kullanabiliyoruz ama bu, async makinesinin kendisinden çok tip sisteminin güncellenmesi demek. Waker da biraz daha kullanışlı hale geldi ama o da daha çok std/core tarafındaki iyileştirmelerle ilgili
      Anladığım kadarıyla async Rust’ı hayata geçiren kişiler ciddi biçimde tükenmişlik yaşadı ve etkinlikleri azaldı; sonrasında da bunu devralan pek çıkmadı. Yine de Google tarafındaki kişilerin, yakalanan değişkenlerin bellek yerleşimini optimize eden bir PR açmış olması sevindirici
      Ben ve iş arkadaşlarım async’i çok kullandığımız için belki de bunu ya doğrudan yapmamız ya da en azından başlatmamız gerekecek. “Bedava” burada daha çok yavru köpek sahiplenmenin “bedava” olması gibi
      Bu yüzden başlığın biraz clickbait olduğu doğru ama yine de onu geri çekmeyi düşünmüyorum
    • Başlığın fazla abartılı olduğuna katılıyorum
      Yazar küçük fonksiyonların ek yüküne gereğinden fazla takılmış gibi görünüyor. “panic” ve “returned” durumlarının ek yükünden rahatsız ama bunlar büyük meseleler değil
      Yararlı async blokların çoğu yeterince büyük olduğundan hata durumu ek yükü arada kaybolur
      Yetersiz inlining konusunda bir noktası olabilir. Ama çok sayıda etkinliği sınırlayan şey çoğunlukla her etkinliğin ihtiyaç duyduğu durum alanıdır
  • async genel olarak bana yeterince olgunlaşmamış bir fikir gibi geliyor. Sıradan kod zaten asenkrondu
    Bir async işi beklemeniz gerekiyorsa thread hazır olana kadar uyur ve çekirdek bunu soyutlar. Sonra insanlar kodu mantıksal thread’ler halinde kurmaktan hoşlanmadıkları için olay tabanlı callback sistemleri eklendi; ardından callback’lerle akıl yürütmenin zor olduğu ve sıralı kontrolün daha iyi olduğu fark edildi
    Bu yüzden thread’lerin doğru programlama modeli olduğunu düşünüyorum
    Şimdi dil runtime’ları taşınabilirlik ve performans nedeniyle “green thread”leri tercih ediyor ama çoğu dil bunu düzgün sunmuyor. Bunun yerine async/non-async renk sorunu, zamanlama, öncelik, preemption olmaması gibi dertler çıkıyor. Bu, 1970’lerden bile kötü bir zamanlama ve süreç modeli

    • “Sıradan kod zaten async’ti; beklerken thread uyur ve çekirdek bunu soyutlar” demek doğru değil
      async kod da çoğu zaman ifade edebileceği eşzamanlılığın tamamını kullanamayacak şekilde yazılır. Örneğin “N tane G/Ç işini aynı anda başlat” yerine “her X için await process(x)” gibi yazılır
      Ama thread dünyasında bu eşzamanlılık sorunu daha da büyür. Thread’ler doğaları gereği fazla ağırdır; bu yüzden eşzamanlılığı verimli ifade etmek zordur ve bunu iyileştirecek bir optimizasyon yönü de yoktur
      Bu yeni bir ders değil. Work-stealing executor’ların, geleneksel thread’lere göre çok daha düşük gecikme ve daha tutarlı P99 verdiği uzun zamandır biliniyor. Apple’ın 2000’lerin başında GCD’yi yapma nedeni de buydu
      Thread’ler, çekirdek zamanlayıcısına iş yükünü anlaması için gereken daha zengin bilgiyi vermez; çekirdek thread’leri de ince taneli eşzamanlılık elde etmek için gereğinden ağır bir mekanizmadır. Salt hesaplama yerine G/Ç ya da karma iş yüklerinde bu daha da kötüdür
      Her programın bu performans düzeyine ihtiyacı yok, ama aynı çabayla daha yüksek performans çıtasına ulaşmak çok daha kolay; nitekim geleneksel yaklaşımın yetişmekte zorlandığı gecikme ve throughput değerlerine ulaşabiliyorsunuz
      async’in yön olarak doğru olduğuna dair bir işaret de io_uring. Çekirdeğin yüksek performanslı G/Ç yaklaşımı olan io_uring, geleneksel thread ve sistem çağrılarından tamamen farklı; tamamlanma işleme modeli de async eşzamanlılığa çok daha yakın. Yine de async/await tek başına async görevler arasındaki ilişkileri ifade etmek için yeterli “renk” sunmadığından, bundan tam yararlanmak daha zor olabiliyor
    • Çekirdek ve OS zamanlayıcısı işin içine girdiği anda, olması gereken hızın 3-4 mertebe altına düşebiliyorsunuz
      En son coroutine/zamanlama koduyla uğraştığımda, hemen sonlanan bir thread oluşturup join etmek yaklaşık 200µs sürüyordu; kendi green thread’imi oluşturup zamanlayıp beklemek ise yaklaşık 400ns alıyordu
      Birileri yine absürt derecede karmaşık bir async framework tasarlayana kadar 10 yıl beklemenize gerek yok. Herhangi bir sistem dilinde 20 satır assembly ile kendi green thread / stack’li coroutine yapınızı kurabilirsiniz
    • “Thread’ler doğru programlama modelidir” sözü ne yaptığınıza bağlı. Hesaplama ağırlıklı işler için thread uygunken, bant genişliği ağırlıklı işler için async daha uygun
      Bant genişliği odaklı kod optimizasyonu aslında zamanlama tasarımı meselesi. Klasik çok iş parçacıklı modelde zamanlamayı sınırlı biçimde kontrol edebilirsiniz; async modelde ise neredeyse kusursuz kontrol mümkündür
      İyi optimize edilmiş bir async zamanlama, aynı bant genişliği ağırlıklı işte eşdeğer çok iş parçacıklı bir mimariden açık ara daha hızlı olabilir
      Bugün yüksek performanslı kodun büyük kısmı bant genişliği ağırlıklıdır ve async de bu tür iş yüklerini daha kolay optimize etmek için vardır
    • Ben callback’lerle akıl yürütmenin daha kolay olduğunu düşünüyorum
      Eşzamanlı işlemleri test ederken ve yarış koşullarının doğru ele alınıp alınmadığını doğrularken, callback’ler zamanlamayı kontrol etmenizi sağladığı için çok daha kullanışlıdır. Her callback ayrı bir birimi temsil ettiğinden hangi olayların yer değiştirebileceğini görebilir ve farklı sıralamaları daha rahat inceleyebilirsiniz
      Thread’lerde ise sıralamayı göz ardı etmek kolaydır; ayrıca başka thread’lerdeki karmaşıklığın mevcut thread’i ne zaman etkileyebileceğini düşünmemeye meylederiz. Bu sadelik değil, daha çok aşırı basitleştirmedir
      Ayrıca yapay bariyerler ekleyip thread’leri durdurmadıkça ya da G/Ç’yi stub’larla değiştirip sıralamayı kontrol eden callback’li mock’lar geçmedikçe eşzamanlı senaryoları gerçekten değiştirip test etmek zordur
      Callback’lerin asıl sorunu, yakalanan çağrı yığınının mantıksal çağrı yığını olmamasıdır. Bazı kütüphaneler/runtime’lar bunu anlamlı kılmak için uğraşsa da, iyi hata tanımları gerekir
      Elbette iki paradigmayı karıştırıp ikisinin de sadece kötü yanlarını alma ihtimaliniz de var
    • Thread’ler async+callback’ten daha iyi ya da daha kötü değil; farklı bir model. Thread’lere çok iyi uyan problemler de var, async ile ifade etmenin çok daha uygun olduğu problemler de
  • Rust’ın ana hedefi güvenlikse neden panic olduğunu anlamıyorum. Kodda panic’e yol açabilecek hiçbir yol olmadığını ispatlayabilmeliyiz
    Bu hafta boyunca buna baktım ve asla panic etmeyeceği garanti edilen bir program yazmanın çok zor olduğunu gördüm. Anladığım kadarıyla panic handler yaklaşık 300KB ve bunu dışarıda bırakmanın tek yolu, derleme anında kodda panic edebilecek hiçbir yol olmaması. Derlemeden sonra panic handler’ın ikili dosyada yer alıp almadığını kontrol etmek biraz hack gibi hissettiriyor
    unwrap ve diğer panic işlemlerini lint’lerle engelleyebilirsiniz ama eğer no-panic Rust alt kümesi olsaydı, bu yazıda değinilen sorunların önemli bir kısmı ortadan kalkardı
    Gerçekte bit flip gibi durumlar dışında yaşanmayacak şeyler için bile teorik olarak panic edebilen çok fazla işlemin olması sinir bozucu. Bir dizinin boş olmadığını ispatlamak ya da async ile uğraşmak da aynı şekilde
    Sonunda ya hiç olmayacak durumlar için tonla hata işleme yazıyor ya da ilk alan ile geri kalan listeyi ayıran boş olmayan liste deseni gibi tuhaf yapılara başvuruyorsunuz. Üstelik bunlar da kendi şişkinliklerini getiriyor

    • Rust-in-Linux tarafında, başarısız olabilen bellek işlemleri gibi konularla bu soruna çözüm aranıyor. Onlar için bu gerekli bir özellik
      Bir dizinin boş olmadığını kanıtlama gibi şeyler dâhil, kanıta dayalı kullanımı artırmaya yönelik işler de yavaş yavaş ilerliyor
    • panic, kullanılabilirlik ve güvenlik açısından oldukça önemli
      panic olmaz ve her durumda yürütmenin devam etmesi gerekirse, invariants bozulmuş bellek bozulmaları gibi durumlarda toparlanabilmek için invariant kontrolü yapılan her yere bol miktarda hata işleme eklemeniz gerekir
      Bu da tam olarak kaygı duyduğunuz şeyle aynı türde bir sorun yaratır: neredeyse hiç olmayacak durumlar için devasa hata işleme yükü
    • Rust’ın hedefi bellek güvenliğidir. panic, bellek güvenliği açısından tamamen güvenlidir
    • Programı çalıştıran OS bile kusursuz değil
      Aracın her şeyi hatasız hale getirmesini bekleyip kendiniz bir şey yapmaya yanaşmama tavrı yorucu geliyor. Kolay API istiyor insanlar; o yeterince kolay değilse YAML ile “programlanan” Kubernetes container’larını, o da kolay değilse GCP ya da Amazon’un tıkla-kullan barındırma hizmetlerini istiyorlar
      Sonunda programlama yapmak değil, hata vermeyen uygulamalar tüketmek ister hâle geliyorsunuz; o yaşam tarzı da bir şeyler üreten insanlarla kurulan simbiyotik ilişkiye dayanıyor
  • Bu tür çirkin ama gerekli tartışmalar C++ tarafında da bir süredir yaşanıyor
    Rust’a async eklendiği andan itibaren onun bulaşıcı doğasından hoşlanmamıştım
    Rust’ın başarılı olmasını istiyorum; böyle insanların sayısı artarsa Rust’ın geleceği de daha parlak olabilir

  • Yakın zamanda Rust async çalışmaya başladım; şu an yaşadığım başlıca sorun kod tekrarı
    Hem asenkron API hem de blocking API desteklemek istediğiniz her fonksiyonu iki kez yazmanız gerekiyor. maybe-async gibi bir şey güzel olurdu
    Bunu aşmak için maybe-async, bisync gibi crate’lere baktım ama hepsinde ya sorunlar ya da ciddi kısıtlar vardı

    • async ya da const gibi anahtar sözcükler açısından fonksiyonları jenerik yapmayı amaçlayan keyword generics çalışması sürüyor
      Şu anda hem senkron hem asenkron dünyada yaşamak isteyen kod için en iyi seçenek sans-io. Fireguard’dan Thomas Eizinger bu desen hakkında güzel bir yazı yazmıştı[1]
      Bu desen yalnızca sync/async sorununu temiz biçimde çözmekle kalmıyor, testleri de kolaylaştırıyor ve DST gibi tekniklere giden yolu da açıyor[2]
      Benim de bu konuda bir yazım var[3]; orada sorunun yalnızca async ve sync ayrımı değil, farklı yürütücüler arasındaki daha geniş bir mesele olduğunu vurguluyorum
      0: https://github.com/rust-lang/effects-initiative
      1: https://www.firezone.dev/blog/sans-io
      2: https://notes.eatonphil.com/2024-08-20-deterministic-simulat...
      3: https://hugotunius.se/2024/03/08/on-async-rust.html
    • Gerçekte ne yaptığınıza çok bağlı, ama yeterince basitse türleri ve await’leri değiştirip takan bir makro yazmak mümkün olabilir
    • Bu, klasik function coloring sorunu. https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
    • Benim bakış açıma göre async fonksiyon zaten maybe-async
      fn -> void ile fn -> Future arasındaki fark, ilkinin hemen sonuna kadar çalışması; ikincisinin ise daha sonra tamamlanabilmesi
      Bir async fonksiyonu blocking biçimde çalıştırmak istiyorsanız, blocking bir yürütücü kullanmanız yeterli
  • Bu yazıyı sevmemin bir nedeni de 2026 Rust hedeflerine kadar uzanması
    Ekipte Rust kullanıyoruz ama ihtiyaç duyduğumuz işleri yapmak için çok derine inmemiz gerekmedi. Yine de topluluk geri bildirimi yüksek olan bir dilin tabandan gelişmesini izlemek keyifli
    C++’ta bunu pek hissetmedim; diğer alanlarda işlerin nasıl yürüdüğünü de pek bilmiyorum
    Tek hoşuma gitmeyen nokta, her hedef için belirli bir finansman gerekiyor gibi görünmesi; biraz Kickstarter hissi veriyor. Şu an bulunan en iyi model gerçekten bu mu diye merak ediyorum

    • “Proje hedefi” ifadesi, gerçekte olduğundan daha yanıltıcı
      Proje hedefi, bir kişinin ya da küçük bir grubun belli bir işi yapmak istediğini belirtmesi ve Rust projesi gönüllülerinden kod incelemesi ya da soruları yanıtlama gibi sürekli destek zamanı istemesi için kullanılan bir sistem
      Bu, Rust projesinin o hedefi resmen benimsediği ya da mutlaka desteklediği anlamına gelmiyor
      Bu yüzden bunu Rust’ın resmî yol haritası gibi görmek doğru değil; daha çok “bu alanda çalışmak isteyen katkıcılar var” şeklinde anlamak gerek
    • C++ ISO komitesi içinde bile, dilin evrim sürecinin bir ölçüde bozulduğuna dair bir uzlaşı var gibi görünüyor. Bunun nedeni çoğunlukla ölçek ve örgütlenme biçimi
      Bir teknoloji ticari olarak yerleşince ne yazık ki işlerin bu yöne kayması normal görünüyor. Büyük sponsorların yalnızca ilgilendikleri alanları finanse etmesini eleştirmek de kolay değil
      Neyse ki TweedeGolf’un kayda değer finansmanının Hollanda hükümetinden geldiğini biliyorum
    • Açık kaynak işlerinde kabaca iki tür var gibi: özellik geliştirme ve bakım
      Yeni özellikler “satılabilir”. Geliştirmek para ister ama gerçek sorunları çözer; eğer o sorunun maliyeti özellik geliştirme maliyetinden büyükse şirketler genelde ödeme yapmaya razıdır
      Bakım daha zor ama artık bakım fonları da var. Örnek olarak RustNL fonu: https://rustnl.org/maintainers/
      Bu tür fonlar daha geniş ve sürekli işleri destekler; çeşitli kuruluşlar az az katkı sunarak bunları ayakta tutar
      Bunun en iyi model olup olmadığını bilmiyorum ama en azından bir ölçüde çalışıyor gibi görünüyor
  • Rust Async ve Tokio belgelerini okursanız, CPU yoğun bölümleri neden async yığınına koymamanız gerektiği, std::sync::Mutex gibi temel araçları async bloklarda nasıl verimli kullanacağınız ve senkron kod ile async kodu nasıl birleştireceğiniz gayet iyi anlatılıyor
    Birçok kod tabanı verimlilikle ilgilenmiyor ya da buna ihtiyaç duymuyor, bu yüzden bu yönergeleri izlemiyor. Ama performans ve verimliliği önemseyen çok sayıda proje var ve kod üretimde çalışmaya başlayınca bu tuzaklar fark ediliyor. ScyllaDB bunun bir örneği
    LLM’ler de yardımcı olmuyor. Her şeyi maine kadar async üretiyor, yanlış temel araçları kullanıyor ve sistemi doğru tasarlamıyorlar

  • Yinelemeli durum katlaması, yani process_command örneğindeki gibi match’i await dallarının dışına çekme deseni, bugün mevcut async koda herkesin uygulayabileceği en kolay yöntem
    Derleyici çalışması gerektirmiyor; yalnızca refaktör etmek yeterli

    • En azından bunun uygulanabileceği yerleri bulan bir özel lint gerekli olurdu. Bu da neredeyse derleyici çalışması sayılır
  • “Future’lar kolay inline edilmez” kısmıyla ilgili olarak, kendi yaptığım bir programlama dilinde async fonksiyon içindeki async fonksiyon çağrılarını inline eden özel bir pass yazdım
    Genel olarak iyi çalışıyor ve bazı boilerplate’leri ortadan kaldırıyor, ama ortaya çıkan ikili dosya boyutu ciddi biçimde büyüyor
    Teknik olarak Rust da aynısını yapabilir

 
GN⁺ 2026-05-06
Lobste.rs görüşleri
  • Sadece başlığa bakınca beklediğimden çok daha yapıcı bir yazıymış

    • Bana göre bu neredeyse düpedüz gerçek. MVP’nin çıkışının üzerinden 7 yıl geçti ama dil tasarımı ya da derleyici implementasyonu tarafında neredeyse hiç ilerleme olmadı; MVP’yi büyük ölçüde ortaya çıkaran kişiler de benzer bir dönemde projedeki faaliyetlerini azaltınca sonrasında devir teslim adeta durdu
      Umarım bu işi yapmak isteyen kişi ihtiyaç duyduğu desteği alır
  • I want to work on this in the compiler and as such have submitted it as a Project Goal

    Stop generating statemachines that don’t have to be there
    Make the compiler’s job easier by removing panic paths and branches
    Make statemachines smaller

    Bu sorunun ele alındığını görmek güzel. Şu anda rustc’nin LLVM’e fazla miktarda kod verdiğini ve optimizer’ın her şeyi halletmesini beklediğini söyleyen birkaç yazı görmüştüm; özellikle bu yazı bu iş için finansman da talep ediyor

  • Aman Tanrım, ben aptalmışım
    async’in bir şekilde runtime, iş takibi ve tamamlanmayı kontrol eden polling gerektirdiği için özünde hep “şişkin” olduğunu düşünüyordum. Sonuçta bu overhead sıfır değil
    Burada sözü edilen “sıfır maliyetli soyutlama”nın dil özelliğiyle ilgili olduğunu, sonradan eklenen runtime’dan ayrı düşünmek gerektiğini varsayıyordum
    LLVM’e vermeden önce rustc’nin gerçekte ne ürettiğine bakmayı hiç düşünmemişim

  • async Rust’a aşina olmayanlar için:

    It's amazing how we can write executor agnostic code that can run concurrently on huge servers and tiny microcontrollers.

    Bu gerçekten doğru. İç içe geçmiş async çağrı ağaçları, maksimum optimizasyondan sonra içinde durum makinesi bulunan tek bir struct hâline kadar katlanabiliyor. Gerçekten çok zekice bir yaklaşım

  • Release build’de bu duruma gelindiğinde bir tür deadlock mu oluşuyor? Yoksa sürekli Pending dönen bir işi bekleyen task’lar yüzünden sızıntı da olabilir mi?

    • Evet. Bu tür future’lar takılı kalmış olur ve asla tamamlanmaz. Ama böyle bir duruma zaten ancak hatalı düşük seviyeli async kodda ulaşılabilir; tamamlanmış future’ları düzgün takip edemeyen kod büyük ihtimalle zaten sızıntı ve deadlock üretiyordur
      .await ile yanlış polling yapılamaz
  • Aklıma birkaç şey geliyor:

    1. Bu yazı, daha fazla optimizasyon mantığının LLVM dışına çıkarılıp MIR katmanına taşınması gerektiğini savunuyor gibi görünüyor. Örneğin async fonksiyon inline etmenin neden LLVM’den ziyade MIR’de daha kolay olduğunu anlıyorum. Eğer bu async için MIR’de yapılabildiyse, acaba bu mantık senkron fonksiyonlara da genellenip LLVM’in bazı optimizasyon pass’leri kaldırılabilir mi diye düşünüyorum. Bunun büyük bir iş olduğunu biliyorum; bu daha çok pratik bir sorudan ziyade bir yön tartışması. Frontend/middle-end derleyici yeterince karmaşıklaştığında, LLVM’in genel amaçlı optimizasyonlarının kayda değer bir kısmının başka katmanlara taşınması daha iyi olabilir gibi geliyor
    2. Hâlâ panic=unwind tercihini sevmiyorum. Bazı test harness’leri dışında, panic=abort yerine bunu seçmenin maliyeti karşılayacak kadar büyük bir faydasını neredeyse hiç görmedim. Hatta test harness için bile Linux’ta pthread_join yerine çalışan thread’i wait etmek için tuhaf bir şekilde clone kullanılarak benzer bir tercih yapılabileceğini düşünüyorum. Bu noktada yanılıyor olabilirim
  • Link başkasında da az önce bozuldu mu?
    Düzeltme: blog yazısı yaklaşık yarım saniye görünüp sonra 404 sayfasına düşüyor
    Düzeltme 2: Blog yazıları listesine gidip etrafa tıkladım; listede duran o yazıyı açınca da 404 sayfasına gidiyor. Statik bir sayfa olan ya da en azından öyle olması gereken bir blogu nasıl bu kadar bozabilirsiniz?

    • Üslup biraz gereksiz yere kaba ve saldırgan geliyor. Web sitelerinde de bug olabilir; bildirmek faydalı ama bu yorum biraz huysuzca tınlıyor
      Bu arada aynı yeniden üretim adımlarını izledim sanırım ama bende hiç 404 çıkmadı. Telefonda ve masaüstünde, JavaScript açıkken de kapalıyken de denedim. Yani yaşadığınız şey göründüğünden daha karmaşık olabilir