1 puan yazan GN⁺ 4 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • Ante, referans sayımının esnekliği ile ödünç denetiminin güvenliğini birlikte kullanırken Rust tarzı çalışma zamanı panic’lerinden veya Swift tarzı münhasır erişim denetimi ek yükünden kaçınmayı amaçlayan bir sistem dili tasarımıdır
  • Temel mekanizmalar shape-stability ve temporary uniq conversion’dır; bunlar referans sayımlı değerlerin alanları üzerinde güvenli biçimde değiştirilebilir ödünçler oluşturmayı ve union içindeki değerleri yalnızca sınırlı kapsamda uniq olarak ele almayı sağlar
  • Rust’taki Rc<RefCell<T>> yanlış kullanıldığında çalışma zamanında panic üretebilir ve Swift’in borrowing system’i çalışma zamanında münhasır erişim denetimleri içerir; Ante ise bazı durumları derleme zamanı kurallarıyla ele almaya çalışır
  • Hâlâ kısmen uygulanmış bir work-in-progress tasarımdır ve belirli bir nesneye türleri özyinelemeli olarak analiz ederek ulaşılıp ulaşılamayacağını belirlemek gerektiğinden, alan eklemek breaking API change olabilir
  • Bu yaklaşım, shared mutable borrowing’in her zaman imkânsız olduğu varsayımını zayıflatıyor ve Vale, group borrowing, Rust GhostCell gibi tekniklerle birlikte bellek güvenliği tasarımındaki istisna alanını genişletiyor

Ante’nin hedeflediği birleşim

  • Ante, bellek güvenliği ve iş parçacığı güvenliği olan daha basit bir Rust olmayı hedefleyen bir sistem programlama dilidir
  • Temel model tek sahiplik ve ödünç denetimidir; değerler stack üzerinde ya da onları içeren struct ve array’lerin içinde inline olarak bulunur
  • Basitliğe öncelik vermek istendiğinde, türe shared anahtar sözcüğü eklenerek referans sayımı seçilebilir
  • shared type Color, shared type RbTree t kullanan red-black tree balance fonksiyonu, Python örneği kadar kısa ve C++ ile Rust örneklerinden daha küçüktür
  • Asıl ilgi noktası, referans sayımlı veriyi değiştirilebilir olarak ödünç alırken Rust’taki borrow_mut() panic riski veya Swift’teki çalışma zamanı münhasır erişim denetimleri olmadan bunun nasıl yapılacağıdır
  • Ante hâlâ work-in-progress durumundadır; bazı kısımları uygulanmıştır, bazıları teoriktir ve tasarım hâlâ değişmektedir

shape-stability ve birden çok değiştirilebilir referans

  • Ante’deki shape-stability, “kararlı şekle sahip bir hedefe verilen referans, başka yerde hangi değişiklik olursa olsun her zaman geçerlidir” fikridir
  • Bu kavram sayesinde aynı struct için aynı anda birden fazla değiştirilebilir ödünç referansa sahip olunabilir
  • heal (healer: mut Entity) (target: mut Entity) örneğinde, aynı Entity’yi iki argüman olarak geçirip kendini iyileştiren self_heal çağrısı yapılabilir
    • healer ile target aynı Entity’yi gösterse bile, bu kodda Entity yok edilemeyeceği için iki referans da geçerliliğini korur
  • Struct’ın kendisine, alanlarına ve alanların alanlarına yönelik değiştirilebilir referanslar da aynı anda izinli olabilir
    • ship: mut Spaceship ile engine_alias: mut Engine = ship.engine aynı anda kullanılsa bile, fonksiyon çalışırken ship ve içindeki engine’in yok edilmeyeceği kabul edilir
  • Rust ve Swift, aynı veriye aynı anda yönelen birden fazla &mut referansına izin vermez

Referans sayımlı değerlerin alanlarını değiştirilebilir ödünç almak

  • Ante’de tür tanımının önüne shared konursa o tür otomatik olarak referans sayımlı olur
  • shared mut type Spaceship örneğinde launch, Rc karşılığı olan Spaceship’i elinde tutarken mut ship.engine ifadesini set_fuel’e geçirir
  • launch kapsayıcı nesne olan Spaceship’i tuttuğu için, onun alanı olan engine’in de yaşadığı sonucuna varılabilir
  • Genel kural, shared mut türlerin alanları için her zaman mut ödünç referansı oluşturulabilmesidir
    • Ancak bu, o alanların içindeki tüm hedefler için her zaman değiştirilebilir ödünç oluşturulabileceği anlamına gelmez; ek kurallar gerekir
  • Sonraki örneklerde sugar syntax olan shared mut type Spaceship yerine daha açık Rc Spaceship gösterimi kullanılır
    • shared mut type Spaceship, type Spaceship olur; var ship: Spaceship ise var ship: Rc Spaceship anlamına gelir

Union’ların güvenlik sorunu yarattığı nokta

  • Union’lar içeriği inline tuttuğu için pointer takibini ve cache miss’leri azaltabilir; bu da hız avantajı sağlar
    • struct Spaceship içindeki C tarzı union Engine, StringTheoryEngine ve ImpulseEngine değerlerini Spaceship belleğinin içinde tutar
    • Bu, Java’daki interface ve pointer kullanılan yaklaşımla karşılaştırılır
  • Sorun, bellek güvenli dillerde union’ları güvenli biçimde desteklemenin zor olmasıdır
  • Engine’in StringTheoryEngine(str: String) veya ImpulseEngine(fuel: I32) olduğu örnekte, ship ile other_ship aynı Spaceship’i gösterdiğinde segfault oluşabilir
    • match uniq ship.engine ile string’in iç kısmına referans alındıktan sonra
    • other_ship.engine := ImpulseEngine 0x42 ile aynı motor başka bir varyanta çevrilir ve ardından
    • eski str değiştirilirse, kapsayıcı yok edildikten sonra iç kısmını kullanma sorunu doğar
  • Bu yüzden Ante, değiştirilebilir ödünç referans bir union’ı gösterdiğinde, onun varyantlarından birinin içine değiştirilebilir ödünç referansı oluşturulmasına izin vermemelidir
  • Bu, struct kurallarının tersidir
    • Bir struct’a mut referansı varsa alanlarına mut referansları üretilebilir
    • Bir union’a mut referansı varsa varyantın içine mut referansı üretilemez

uniq ve temporary uniq conversion

  • uniq, exclusive mutable reference, yani münhasır değiştirilebilir referans anlamına gelir
  • Bir değişken uniq Spaceship tutuyorsa, bu o Spaceship için kullanılabilir tek referanstır
    • Kavram olarak Rust’taki &mut Spaceshipe benzer
  • Ante, union içini güvenli biçimde ele almak için temporary uniq conversion kullanır
  • Temel kural şudur: Belirli bir kapsam boyunca başka olası takma adlı referanslar kullanılmıyorsa geçici olarak uniq referansı alınabilir
    • match uniq ship.engine bloğunda ship.engine, uniq gibi ele alınır
    • Bu kapsam boyunca derleyici, Spaceship’i dolaylı olarak içerebilecek diğer mevcut değişkenlerin kullanılmasına izin vermez
  • Rust, “başka referanslar bir yerlerde bulunuyor olabilir” diye uniq varlığının kendisini engellerken, Ante bu referanslar o kapsamda kullanılmadığı sürece uniqe izin verir
  • Bu durumda uniq Spaceship, gerçekte küresel olarak tek referans değil; yalnızca o kapsam içinde kullanılabilen tek referanstır
    • Bu, C’deki restrict pointer’ına benzer bir nüans taşır

İzin verilen ve reddedilen erişimler

  • match uniq ship.engine kapsamı içinde other_ship: Rc Spaceship erişimi bir derleme hatası üretmelidir
    • çünkü other_ship.engine, ship.engine ile alias olabilir ve
    • ship.engine kullanılırken other_ship.engine üzerinde yapılacak değişiklik drop’a yol açabilir
  • HasAShip gibi Rc Spaceship alanı olan başka struct’lar da aynı nedenle reddedilir
    • çünkü other.ship.engine de dolaylı olarak aynı Spaceship’e ulaşabilir
  • Buna karşılık new_fuel: I32 gibi tamsayılar kullanılabilir
    • çünkü I32, Spaceshipe referans içeremez
  • Spaceship’in kendisinde follow_ship: Rc Spaceship gibi alanlar varsa bu da reddedilir
    • çünkü bu durumda uniq Spaceship, kendi iç yolundan tekrar erişilebilir hâle gelir; dolayısıyla genel olarak özyinelemeli türlerde mut -> uniq dönüşümü yapılamaz

Fonksiyon çağrıları ve dönüşlerdeki kısıtlar

  • mut -> uniq dönüşümü fonksiyon çağrılarında da olabilir
  • foo (var ship: Rc Spaceship) (new_res: Resonator) fonksiyonu maybe_use_resonator ship new_res çağrısı yaptığında, çağrı noktasında ship bir uniq Spaceshipe dönüştürülür
    • Derleyicinin yalnızca diğer argümanların Spaceship referansı içerebilip içermediğini kontrol etmesi gerekir
    • Örnekteki Resonator böyle bir referans içermediği için buna izin verilir
  • Dönüşte, dönüştürülmüş uniq referansı normal uniq olarak geri döndürülemez
    • çünkü dönüşten sonra “bu kapsamda alias olabilecek değişkenler kullanılmıyor” denetimi artık uygulanamaz
  • Bunun yerine dönüş tipi local uniq Foo olarak belirtilebilir
    • Dahili olarak, mut reften uniq refe dönüştürülürken gerçekte her zaman local uniq üretilir
    • Çoğu durumda normal uniq gibi kullanılabilir, ancak döndürülürken bunun açıkça belirtilmesi gerekir

Tasarım maliyetleri ve alternatifler

  • Ante, Rc Spaceship gibi referans sayımlı referansları çalışma zamanı hatası olmadan geçici uniq Spaceshipe çevirebilir
  • Dezavantajı, derleyicinin “Engine içinden Spaceship’e ulaşılabilir mi?” gibi soruları yanıtlamak için türleri özyinelemeli olarak incelemek zorunda olmasıdır
  • Bu tür analizler kırılgan olabilir
    • Bir struct’a alan eklemek breaking API change olabilir
  • Ante’nin yaratıcısı Jake, bu garantiyi korumanın daha iyi yollarını arıyor
    • group borrowing ve Flix references benzeri şekilde, her paylaşımlı değiştirilebilir türe anonim ve benzersiz bir brand type ekleme yaklaşımı
    • Paylaşımlı tür değiştiğinde Mutates 'a gibi bir effect ekleyerek tür analizini ortadan kaldırma yaklaşımı
    • İki referansın farklı nesneleri gösterip göstermediğini kullanıcının çalışma zamanında denetlemesi veya unsafe denetimi safe API ile sarmalama yaklaşımı
    • Derleyicinin, Rc içinde dolaylı saklanmadığı için alias olamayacak değerleri izlemesi yaklaşımı
  • Pony’nin iso permission yaklaşımına benzer fikirler ya da struct’ın içini görüp dışarıyı gösteren referansların kullanımını engelleyen geçici izinler de olası seçenekler arasında
  • Zor olan nokta, bu esnekliği korurken Ante’nin hedeflediği kullanılabilirlik, okunabilirlik ve basitlik özelliklerini sürdürmektir

Daha geniş bellek güvenliği eğilimi

  • shared mutable borrowing, eskiden imkânsız sayılıyordu ve Rust’ın da bu inanç üzerine kurulduğu varsayımı burada arka planda yer alıyor
  • Ancak çeşitli istisnalar birikiyor
    • Ante, local uniqueness kurallarıyla shared-mutable veriden uniq ödünç referansı elde edebiliyor
    • Vale, pure function yoluyla shared-mutable veriden değiştirilemez ödünç referansı elde edebiliyor
    • group borrowing, shape-stable olmasa bile shared-mutable ödünç referansları oluşturabiliyor
    • Rust’ın GhostCell yaklaşımı, nesne grafiklerinin birbirini serbestçe göstermesine izin verirken belirli bir anda bunlardan yalnızca biri için tek bir değiştirilebilir referans tutulmasına izin veriyor
  • Bu eğilim, bellek güvenliği tasarımında shared mutable borrowing’i ele alan daha genel bir ilke olabileceğine işaret ediyor

Rust Cell ile karşılaştırma

  • Rust kullanıcıları, struct alanlarına Cell koyma yaklaşımı ile Ante’nin yaklaşımı arasındaki farkı sorabilir
  • Ante örneğinde, Rc Spaceship içinden status: String alanına mut String referansı alınıp doğrudan " (refueling)" eklenebilir
  • Rust’taki Cell<String> yaklaşımında Rc<Spaceship> içinden &mut String alınamaz
    • Bunun yerine status_ref.replace(String::new()) ile geçici varsayılan değer konur
    • Çıkarılan String değiştirilir ve
    • Son olarak replace(status) ile geri konur
  • Bu yaklaşımın bazı dezavantajları vardır
    • "" gibi bir varsayılan örnek oluşturmak gerekir
    • Son replace çağrısını unutma riski vardır
    • Değer değiştirilmiş durumdayken bir başkasının status alanını okuma riski vardır
  • Ante, geçici olarak status string’ine referans alınmasına izin verir ve bu sırada başka kodların erişimini derleyici zorunlu olarak engeller

1 yorum

 
GN⁺ 4 시간 전
Lobste.rs yorumları
  • Paylaşılan değiştirilebilir ödünç alma”nın imkânsız sayılması, Rust’ın hedefine ulaşmak için katlandığı basit bir fedakârlık değil; Rust’ın temel hedefinin kendisine oldukça yakın bir şey
    Çünkü paylaşılan değiştirilebilir durum, kod hakkında yerel akıl yürütmeyi zorlaştırır
    withoutboats tarafından yazılan "References are like jumps" bu noktayı iyi ele alıyor. Alias’ı olan durumun kazara değiştirilmesini engellemenin, doğru çalışan sistemler kurmayı kolaylaştırmanın anahtarı olduğu; Rust’ın yaşam süresi kurallarının ise yalnızca çöp toplamadan kaçınmaya yarayan bir mekanizma değil, değiştirilebilir durum ile alias’lı durumu birlikte kabul eden bir dilde akıl yürütülebilirliği sağlayan daha derin bir yapı olduğu savunuluyor

    • Yazar daha önce Mojo borrow checker’ı ele aldığında da aynı şeyi düşünmüştüm. Rust’ın ödünç alma denetleyicisi, tek iş parçacıklı programlarda bile değer semantiğini koruyor
  • Oldukça iyi görünüyor
    Doğru anladıysam, paylaşılan referanstan değiştirilebilir referansa geçişteki sihir, iş parçacıkları arasında paylaşılmayan tiplerle sınırlı olduğu için mümkün; Rc’nin benzersizliği de aynı tipteki tüm nesneleri aynı yaşam süresiyle ödünç alınmış gibi ele alarak garanti ediliyor gibi
    Açık söz dizimi mi doğal söz dizimi mi daha iyi, bu bir zevk meselesi olabilir; ancak derleyicinin Cell hakkında daha fazla şey bilmesi durumunda ona yönelik değiştirilebilir referanslara daha esnek biçimde izin verebileceğini gösteriyor
    Ayrıca Rust’ta mutun değiştirilebilir değil dışlayıcı/benzersiz anlamına geliyormuş gibi kullanılmasından doğan kafa karıştırıcı terminolojiden de kaçınıyor

    • İş parçacıkları arasında bunun nasıl olacağını merak etmiştim. “uniq yükseltmesi kilit edinme anlamına mı geliyor?” gibi bir soruydu; ama karşılaştırılan şeyin Arc değil Rc olduğu anlamına geldiğini anlıyorum
    • mutun dışlayıcı/benzersiz anlamına geldiği kısmı biraz daha açıklayabilir misin?
  • Son kısımda ima edilen birleştirici ilkenin ne olabileceğine dair tahmini olan var mı, merak ediyorum

  • antelang.org blog yazılarına ilişkin önceki tartışmalar da bakmaya değer

  • Bunun nasıl çalıştığını pek anlayamadım. “Bir nesneye yönelik değiştirilebilir bir işaretçiniz varsa, o nesnenin bir dilimine yönelik değiştirilebilir bir referans elde edebilirsiniz” demek gibi görünüyor
    Ama öyleyse örneğin mutref someobjext = …, mutref subfield = someobjext.a.b, someobjext.a = somethingelse gibi bir şey mümkün görünüyor; bu durumda subfield geçersiz hâle gelebilir ya da değeri değişip bozulabilir
    Yazıda açıklama, başka dillerle karşılaştırma ve kod örneği çoktu; ancak bu davranışın adım adım semantiğini temel düzeyde düzenli biçimde anlatan kısmı bulmakta zorlandım