Daha iyi bir eşzamanlılık hikayesi, Rust projesinin ana hedeflerinden biridir, bu nedenle projeye hedeflerine ulaşmak için güvendiğimiz sürece iyileştirmeler beklenmelidir. Tam feragatname: Rust hakkında yüksek bir fikrim var ve yatırım yapıyorum. İstendiği gibi, değer yargılarından kaçınmaya ve (IMHO) iyileştirmeler yerine farklılıkları tanımlamaya çalışacağım .
Güvenli ve güvensiz Pas
"Rust" iki dilden oluşur: Sizi, sistem programlamanın tehlikelerinden ayırmaya çok çalışan ve böyle bir özlem olmadan daha güçlü bir dil.
Güvensiz Rust, C ++ 'a çok benzeyen pis, kaba bir dildir. Keyfi olarak tehlikeli şeyler yapmanıza, donanımla konuşmanıza, (yanlış) belleği manuel olarak yönetmenize, kendinizi ayağınıza vurmanıza vb. İzin verir. Programın doğruluğunun elinizde olması, C ve C ++ gibi ve buna dahil olan diğer tüm programcıların elleri. Anahtar kelimeyle bu dili tercih edersiniz unsafe
ve C ve C ++ 'da olduğu gibi, tek bir konumdaki tek bir hata tüm projenin çökmesine neden olabilir.
Güvenli Rust "varsayılan" dır, Rust kodunun büyük çoğunluğu güvenlidir ve anahtar kelimeyi unsafe
kodunuzda asla belirtmezseniz , güvenli dili asla terketmezsiniz. Görevin geri kalan kısmı çoğunlukla bu dille ilgilenecektir, çünkü unsafe
kod, Rust'ın size sağlaması için çok çalıştığı garantilerinin tümünü ve tümünü bozabilir. Kapak tarafında, unsafe
kod kötü değildir ve toplum tarafından böyle muamele edilmez (ancak, gerekli olmadığında kesinlikle önerilmez).
Tehlikelidir, evet, ama aynı zamanda önemlidir, çünkü güvenli kodun kullandığı soyutlamaları oluşturmaya izin verir. İyi güvensiz kod, başkalarının kötüye kullanmasını önlemek için type sistemini kullanır ve bu nedenle bir Rust programında güvensiz kodun bulunması güvenlik kodunu bozmaz. Aşağıdaki tür farklar, Rust'un tip sistemlerinde C ++ 'nın sahip olmadığı araçlara sahip olması ve eşzamanlılık soyutlamalarını uygulayan güvensiz kodun bu araçları etkin bir şekilde kullanması nedeniyle ortaya çıkar.
Farksızlık: Paylaşılan / değiştirilebilir hafıza
Rust, mesaj iletmeye daha fazla önem vermesine ve paylaşılan hafızayı çok sıkı bir şekilde kontrol etmesine rağmen, paylaşılan hafızanın eşzamanlılığını dışlar ve ortak soyutlamaları (kilitler, atomik işlemler, koşul değişkenleri, eşzamanlı koleksiyonlar) açıkça destekler.
Dahası, C ++ gibi ve fonksiyonel dillerin aksine, Rust gerçekten geleneksel zorunlu veri yapılarını sever. Standart kütüphanede kalıcı / değişmez bağlantılı bir liste yoktur. Var std::collections::LinkedList
ama bu std::list
C ++ ' da olduğu gibi ve aynı nedenle std::list
(önbellek kullanımı kötü) aynı cesarete sahip değil .
Bununla birlikte, bu bölümün başlığına ("paylaşılan / değiştirilebilen bellek") atıfta bulunarak, Rust, C ++ 'a göre bir farklılığa sahiptir: Belleğin "paylaşılan XOR değişebilir" olmasını, yani aynı bellekte asla paylaşılmamasını ve değiştirilememelerini şiddetle teşvik eder. saati. Demek istediğin gibi "kendi iş parçanın mahremiyetinde" olarak belleği değiştir. Bunu, paylaşılabilir değişken hafızanın varsayılan seçenek olduğu ve yaygın olarak kullanıldığı C ++ ile karşılaştırın.
Paylaşılan-xor-değişebilir paradigması aşağıdaki farklar için çok önemli olsa da, alışması biraz zaman alan ve önemli kısıtlamalar getiren oldukça farklı bir programlama paradigmasıdır. Bazen, bu paradigmadan, örneğin atomik tiplerden vazgeçmek zorunda kalır AtomicUsize
(paylaşılan değiştirilebilir hafızanın özüdür). Kilitlerin aynı zamanda, paylaşılan-xor-değişken kuralına da uyduğunu unutmayın, çünkü eşzamanlı okuma ve yazma işlemlerini hariç tutar (bir konu yazar, başka konu okunamaz veya yazı yazamaz).
Farksızlık: Veri yarışları tanımsız davranışlardır (UB)
Rust kodunda bir veri yarışını tetiklerseniz, oyun biter, tıpkı C ++ 'da olduğu gibi. Tüm bahisler kapalıdır ve derleyici ne isterse yapabilir.
Bununla birlikte, güvenli Rust kodunun veri yarışlarının (veya bu konuda herhangi bir UB'nin) bulunmaması kesin bir garantidir . Bu, hem ana dile hem de standart kütüphaneye uzanır. unsafe
UB'yi tetikleyen, kullanmayan (üçüncü taraf kütüphanelerinde de dahil olmak üzere standart kütüphaneyi hariç) içeren bir Rust programı yazabilirsiniz , o zaman bu bir hata olarak kabul edilir ve giderilir (bu zaten birkaç kez olmuştur). Tabii ki bu, tabiki C ++ ile karşılaştırıldığında, UB ile program yazmanın önemsiz olduğu durumlar.
Fark: Sıkı kilitleme disiplini
C aksine ++ Rust (bir kilit std::sync::Mutex
, std::sync::RwLock
vs.) sahip olan bu koruduğunu verileri. Kilit almak ve ardından yalnızca belgelerde kilitle ilişkilendirilmiş bazı paylaşılan belleği değiştirmek yerine, kilidi tutmuyorsanız paylaşılan verilere erişilemez. Bir RAII koruması kilidi tutar ve aynı anda kilitli verilere erişim sağlar (bu kadarı C ++ tarafından uygulanabilir, ancak std::
kilitler tarafından değil ). Ömür boyu sistemi, kilidi açtıktan sonra verilere erişememenizi sağlar (RAII korumasını bırakın).
Elbette, yararlı veri içermeyen bir kilitleme ( Mutex<()>
) olabilir ve bu belleği kilitlemeyle açıkça ilişkilendirmeden bir miktar belleği paylaşabilirsiniz. Ancak, potansiyel olarak senkronize edilmemiş paylaşılan belleğe sahip olmak gerekir unsafe
.
Fark: Kazara paylaşımın önlenmesi
Paylaşılan hafızayı paylaşabilseniz de, bunu açıkça istediğinizde paylaşırsınız. Örneğin, mesaj iletmeyi kullandığınızda (örneğin kanalları std::sync
), ömür boyu sistemi, başka bir iş parçacığına gönderdikten sonra verilere herhangi bir referans tutmamanızı sağlar. Bir kilidin arkasındaki verileri paylaşmak için, kilidi açıkça oluşturup başka bir iş parçacığına verirsiniz. Senkronize edilmemiş hafızayı unsafe
sizinle paylaşmak için kullanmanız gerekir unsafe
.
Bu bir sonraki noktaya bağlanır:
Fark: İş güvenliği izleme
Rust'un tip sistemi bir miktar iplik güvenliği nosyonunu izler. Spesifik olarak, bu Sync
özellik, veri yarışları riski olmadan birkaç iş parçacığı tarafından paylaşılabilen tipleri belirtirken Send
, bir iş parçacığından diğerine taşınabilecekleri işaretler. Bu program boyunca derleyici tarafından uygulanır ve bu nedenle kütüphane tasarımcıları bu statik kontroller olmadan aptalca tehlikeli olabilecek optimizasyonlar yapmaya cesaret ederler. Örneğin, birkaç iş parçacığı tarafından kullanılması std::shared_ptr
durumunda UB'yi önlemek için referans sayısını değiştirmek için her zaman atomik işlemleri kullanan C ++ 'lar shared_ptr
. Pas vardır Rc
ve Arc
yalnızca bu farklılık, hangi Rc
kullanımlar olmayan atom refcount operasyonları ve ÅŸan (yani uygulamıyor değil Sync
ya Send
) iken Arc
çok çok gibishared_ptr
(ve her iki özelliği de uygular).
Bir tür eğer Not gelmez kullanmak unsafe
Senkranizasyonu uygulamak, özelliklerin varlığı veya yokluğu doğru çıkarılmaktadır.
Fark: Çok katı kurallar
Derleyici, bazı kodların veri yarışlarından ve diğer UB’lerden arınmış olduğundan kesinlikle emin olamazsa, dönem oluşturmaz . Yukarıda belirtilen kurallar ve diğer araçlar sizi oldukça uzağa götürebilir, ancak er ya da geç doğru olan bir şey yapmak isteyeceksiniz, ancak derleyicinin bildiriminden kaçan ince nedenlerle. Zor bir kilit içermeyen veri yapısı olabilir, ancak "Paylaşılan bir dizide rastgele konumlara yazarım, ancak endeksler her konumun tek bir iş parçacığı tarafından yazılacağı şekilde" hesaplanır.
Bu noktada, mermiyi ısırıp biraz gereksiz senkronizasyon ekleyebilirsiniz veya kodu, derleyicinin doğruluğunu görebilecek şekilde sık sık (bazen yapılabilir, bazen oldukça zor, bazen imkansız) veya unsafe
kodun içine bırakabilirsiniz . Yine de, ekstra bir zihinsel ek yüküdür ve Rust size unsafe
kodun doğruluğu konusunda hiçbir garanti vermez .
Fark: Daha az araç
Yukarıda bahsedilen farklılıklar nedeniyle, Rust'ta bir kişinin bir veri yarışına sahip olabilecek kod yazması (ya da ücretsiz sonra kullanması ya da iki kez serbest olması ya da ...) olması çok nadirdir. Bu hoş olsa da, bu tür hataları izlemeye yönelik ekosistemin toplumun gençliği ve küçüklüğü göz önüne alındığında beklenenden daha az gelişmiş olması talihsiz bir yan etkiye sahip.
Valgrind ve LLVM'nin diş temizleyicileri gibi aletler prensip olarak Rust koduna uygulanabilirken, bunun gerçekten işe yarayıp yaramadığı, takımdan takıya değişebilir (ve işe yarayanlar bile, özellikle de hiçbir şey bulamadığınız için) -tarihi kaynaklar nasıl yapılır?). Rust'un şu anda gerçek bir spesifikasyondan ve özellikle de resmi bir bellek modelinden yoksun olması gerçekten yardımcı olmuyor.
Kısacası, unsafe
Rust kodunu doğru yazmak, her iki dilin de kabiliyetler ve riskler açısından kabaca karşılaştırılabilir olmasına rağmen, C ++ kodunu doğru yazmaktan daha zordur . Elbette bu, tipik bir Rust programının yalnızca nispeten küçük bir unsafe
kod kesri içereceği gerçeğine karşı ağırlıklandırılmalıdır , oysa bir C ++ programı, tam olarak C ++.