İyi gidenler
- Yeniden yazım küçük adımlarla yapıldı (kademeli, stop-and-go), iyi çalıştı ve yeni kod okunup anlaşılması daha kolay hale geldi
- Tüm kodu görme imkânı sayesinde performans optimizasyonu fırsatları elde edildi
- Kullanılmayan kodun yaklaşık 1/3 ila 1/2’si kaldırıldı. Rust veya Go gibi modern programlama dilleri dead code’u daha iyi tespit edip geliştiriciye bildiriyor
- Sınır dışı erişim ya da overflow/underflow konusunda endişe yok
- Yerleşik test framework’ü çok faydalı
- CMake dosyalarını kaldırabilmek sevindirici
İyi gitmeyenler
Hâlâ undefined behavior izlenmek zorunda
- C/C++’tan Rust’a kademeli yeniden yazım yapılırken çok sayıda ham pointer ve
unsafe{} bloğu kullanmak gerekti
- Rust kuralları
unsafe içinde de geçerli, ancak derleyici bunları kontrol etmediği için undefined behavior kolayca oluşabiliyor
unsafe içinde birden fazla salt okunur pointer XOR bir adet değiştirilebilir pointer kuralını bozmak kolay
- Miri bunu yakalayan bir kurtarıcı görevi görüyor
Miri her zaman çalışmıyor ve yine de Valgrind kullanmak gerekiyor
- Kriptografi kütüphaneleri gibi C ya da assembly ile yazılmış bölümleri olan kütüphaneler kullanıldığında Miri çalışmıyor
- Miri tarafından kontrol edilmeyen çok sayıda
unsafe kod var
- Bazı testlerin
valgrind altında çalıştırılması gerekti
Hâlâ bellek sızıntılarını izlemek gerekiyor
- C API’lerinde yaygın bir kalıp,
MYLIB_init() içinde bellek ayırıp MYLIB_release() içinde serbest bırakmaktır; ancak MYLIB_release çağrısını unutmak kolaydır
- Rust geliştiricileri RAII ile wrapper nesneler yapmak ister, ancak C API kullanan testlerde bu özellik kullanılamaz
- Karmaşık mantıkta cleanup fonksiyonlarını her zaman çağırmak zordur. C’de bu
goto ile çözülür ama Rust bunu desteklemez
- Bu sorun
defer crate’i ile çözüldü, ancak borrow checker bundan hoşlanmıyor
Cross-compilation her zaman çalışmıyor
- Miri’de olduğu gibi, C ya da assembly ile implemente edilmiş bölümleri olan kütüphaneler kullanıldığında
cargo build --target=... doğrudan çalışmıyor
Cbindgen her zaman çalışmıyor
- Cbindgen, Rust codebase’inden C header üretmek için çok kullanılır, ancak sınırlamaları veya bug’ları vardır
Kararsız ABI
Option gibi kullanışlı standart kütüphane türleri için kararlı bir ABI olmadığından, repr(C) anotasyonuyla manuel olarak kopyalamak gerekiyor
Custom memory allocator desteğinin olmaması
- Birçok C kütüphanesi, kullanıcının çalışma zamanında allocator sağlamasına izin verir. Rust’ta ise yalnızca derleme zamanında global allocator seçilebilir
- Kaynak temizleme sorunları arena allocator ile çözülebilir, ancak Rust’ta bu idiomatik değildir ve standart kütüphane ile entegre değildir
Karmaşıklık
UnsafeCell, RefCell, MaybeUninit, Pin gibi yapıların FFI işlemleri için kullanılması gerektiğinden karmaşıklık artıyor
- Saf Rust bile zaten karmaşıksa, üstüne FFI katmanı eklenince tam bir canavara dönüşüyor
- Rust’ın karmaşıklığı nedeniyle bu codebase üzerinde çalışmayı reddeden geliştiriciler de oldu
Sonuç
- Rust ile yeniden yazımdan genel olarak memnun kalındı, ancak bazı alanlarda hayal kırıklığı yaşandı ve beklenenden çok daha fazla emek gerekti
- C ile yoğun etkileşim içindeki Rust, saf Rust kullanmaktan tamamen farklı bir dil gibi hissettiriyor. Sürtünme çok fazla ve çok sayıda tuzak var. Rust’ın çözdüğünü iddia ettiği C++ sorunlarının birçoğu aslında hiç çözülmüş değil
- Rust, Miri, cbindgen ve benzeri araçların geliştiricilerine derin teşekkür borçlu olunmalı. Muazzam bir iş çıkardılar. Buna rağmen, yoğun C FFI kullanılan durumlarda dil ve araçlar olgunlaşmamış ve neredeyse v1.0 öncesi gibi hissettiriyor
unsafe ergonomisi, standart kütüphane, dokümantasyon, araçlar ve kararsız ABI ileride iyileşirse çok daha keyifli bir deneyim olabilir
- Görünüşe göre Microsoft ve Google da tüm bunları hissettiği için bu alana gerçekten finansman yatırıyor
- Henüz Rust bilmiyorsanız, ilk projenizde saf Rust kullanmanız ve FFI konularından uzak durmanız iyi olur
- Başlangıçta bu yeniden yazım için Zig veya Odin kullanmak da düşünülmüştü, ancak kurumsal production codebase’de v1.0 öncesi bir dil kullanılmak istenmedi. Şimdi deneyimin Rust’tan gerçekten daha kötü olup olmayacağı merak ediliyor. Muhtemelen Rust modeli C modeliyle (veya C++ modeliyle) gerçekten uyuşmadığı için ikisini birlikte kullanırken sürtünme çok fazla oluyor
- İleride benzer bir iş yapılacaksa Zig ciddi şekilde değerlendirilecek. Biri her "sadece Rust ile yeniden yazın" dediğinde bu yazıyı gösterip fikrinin değişip değişmediğini sormak gerekir
12 yorum
Zig henüz v1 öncesi olsa da birçok C kütüphanesini kullanabildiği için düşünüldüğünden daha kullanışlı. Hâlihazırda çalışan C tabanlı bir projeye bir şeyler eklemek için Zig, Rust'tan daha iyi bir seçenek olabilir.
Rust'a bakarken,
unsafeadlı anahtar kelimeyi gördüğüm anda içime ürpertici bir his gelmişti ve...Rust'ın C++'ın sahip olduğu kronik sorunları çözebileceğini düşünmüyorum. Bu, sözdizimsel bir bakış açısından ziyade pratik bir bakış açısı.
Bunun nedenleri şunlar:
Zaten çok fazla production ortamı C/C++ kullanıyor. Ve bunlar kararlı şekilde gayet iyi çalışıyor. Ayrıca çoğu, bunu özellikle Rust'a port etmeye çalışmıyor.
En başta donanım, reference count varsayımıyla tasarlanmıyor. C/C++ kullanımının birçok nedeni donanımı, işletim sistemini, sürücüleri ve binary katmanını yüksek hızda kontrol etmek; Rust'ı desteklemek için düşük seviyeli geliştiricilerin sonuçta
unsafekullanarak kaynakların yaşam döngüsünü doğrudan yönetmesi gerekiyor ve bunun da maliyeti büyük.Yazarın deneyiminin, dilin barındırdığı değer ve teorik tartışmalardan daha önemli olduğunu düşünüyorum.
Gerçekte C/C++ düzeyinde bir dilin gerekli olduğu alanlardaki kaynak yönetimi seviyesi, Rust ile ikame etmek için ne et ne de balık gibi kalıyor diye düşünüyorum.
Bu yazı da Rust'ı yanlış anlayıp girişmiş gibi görünüyor.
İçeriğe bakınca Rust dışıyla sık sık iletişim kurması gereken, kütüphane benzeri bir yapı gibi; o noktada zaten işin kirlenmemesi mümkün değil... Zaten native diller arasında baştan sona temiz olan pek yok, Rust ise bunu dil seviyesinde güvenli biçimde sarmaladığı için dil dışıyla temas noktaları arttıkça avantajı da büyük ölçüde azalıyor.
Bu belli ölçüde doğru, ama bence asıl sorun, kaynak metindeki geliştirme ortamında bunun çözülemeyecek olmasıyken Rust'a sanki her derde deva bir şeymiş gibi yaklaşılıyor olması.
Ben bunu, C/C++'ı Rust'a kademeli olarak geçirirken
unsafekullanmak kaçınılmaz olduğu için Rust'a geçmenin pek anlamlı olmadığı; Rust'a kademeli geçiş yapmak yerine Zig'i tercih edeceği yönünde hissettim. Peki metnin neresinde, Rust dışıyla sık sık iletişim kurmak zorunda olan bir kütüphane olduğundan bahsediliyor?FFI kullanmak, Rust dışındaki bileşenlerle iletişim kurmak anlamına geliyor.
Ayrıca metnin içeriğine bakınca bunun yalnızca bir durum ya da basit verileri alıp vermekle sınırlı kalmadığı, iç ve dış tarafın karmaşık biçimde etkileştiği görülüyor.
C ile yazılmış bir kütüphaneyi aşamalı olarak Rust’a çevirmek istiyorsanız, FFI kaçınılmaz değil mi? Programın küçük parçalarını Rust’a geçirip kalan C kısmını FFI ile ele almanız gerekecek; burada yapılan işleri dış dünya ile iletişim olarak mı ifade etmişti? Eğer öyleyse, asıl yazarın Rust’a karşı kuşku duyması bana doğal geliyor. Tüm kodu bir kerede değiştirmediğiniz sürece Rust’ın avantajları ortaya çıkmayacağı için muhtemelen bu yüzden Zig’i öneriyordur.
^-^
Kaynak kodda
unsafebölümler açıkça işaretlendiği için, programın giriş noktasından itibarenunsafeblokları kullanılmadığı sürece FFI'nin etki alanının tamamının ayırt edilebildiğinden ve bunun faydalı olmasının beklendiğini düşünmüştüm; ancak anlaşılan bu, yazar için pek de hissedilir bir avantaj olmamış.Zaten FFI kullanıldığı anda güvenli tasarım diye bir şey kalmamış sayılır.
Kesinlikle.
Aynen,
unsafeile baştan sona kaplandığını gayet açık yazmışlar ama yine de çözülmemiş olması...