Any trait'i, Rust'ın gelişiminin erken dönemlerinde dinamik tip yetenekleri sağlamak amacıyla tanıtıldı; bu, derleme zamanında tip bilgilerinin mevcut olmadığı hata yönetimi ve hata ayıklama senaryoları için geçerlidir. Tasarımı, C++'ın typeid veya Java'nın instanceof gibi diğer dillerdeki benzer kavramlarla örtüşmektedir, ancak Rust'ın sahiplik modeli benzersiz kısıtlamalar getirmektedir. 'static gereksinimi, tipleri silen referansların, tanımladıkları verilerden daha uzun sürmemesini sağlamak için ortaya çıktı ve çöp toplama olmayan bir dilde serbest bırakma hatalarını önledi.
'static sınırı olmadan, Any olarak silinmiş bir tip, kısıtlı bir yaşam süresine sahip yerel yığın verilerine referanslar içerebilir. Eğer Any trait nesnesi, o yığın çerçevesinden daha uzun sürerse, aşağıya indirme ve dereferanslama, serbest bırakılmış belleği erişmeye çalışır. Any; vtbl'ler ve tip silme aracılığıyla çalıştığından, derleyici aşağıya indirme sırasında yaşam sürelerini doğrulayamaz; 'static sınırı, tipin tüm verilerini sahip olduğuna veya yalnızca statik referanslar içerdiğine dair temkinli bir garanti sağlamaktadır ve silme sınırında bellek güvenliğini garanti eder.
Any trait tanımı trait Any: 'static, bu kısıtlamayı derleme zamanında uygulamak için Rust'ın trait sınır sistemini kullanır. Yalnızca statik referanslar içermeyen tipler Any'yi uygulayabilir, bu da herhangi bir &dyn Any veya Box<dyn Any>'nin program süresince geçerli olmasını garanti eder. Bu, downcast_ref() ve downcast_mut() aracılığıyla güvenli aşağıya indirme yapılmasına olanak tanır, çünkü altındaki verinin kapsam çıkışları tarafından geçersiz kılınmayacağı garantisi bulunmaktadır.
Bir oyun motoru için eklenti sistemi inşa ediyorduk; burada betikler, ana motora sonradan işlenmek üzere çeşitli veriler döndüren etkinlik işleyicileri kaydedebilirdi. Motor, bu dönüş değerlerini heterojen bir kuyruğa depolamak zorundaydı; bu, farklı tipleri tek bir koleksiyonda saklamak için tip silmesi gerektiriyordu. Ancak bazı betik bağlamaları, betiğin yürütme bağlamı içindeki geçici yerel değişkenlere referanslar döndürmeye çalışıyordu; bu referanslar, betik çerçevesi tamamlandığında asılı hale gelecekti.
Çözüm 1: Hayat süresi parametreleri ile özel trait
Bir yaklaşım, motorun trait nesnesi aracılığıyla yaşam sürelerini takip etmesine olanak sağlayacak hayata süresi parametreleri ile ilişkili bir tip içeren özel PluginResult trait'ini oluşturmayı içeriyordu. Bu, borçlu veri kullanımına izin vererek esneklik vaat ediyordu, ancak tüm eklenti API yüzeyinde karmaşık yaşam süreleri açıklamaları gerektiriyordu. Bu karmaşıklık, her eklenti yazarının ileri düzey Rust yaşam süreleri mekaniklerini anlamasını gerektirecek ve üçüncü taraf kodunda ince yaşam süresi hataları olasılığını artıracaktı.
Çözüm 2: Güvensiz yaşam süresi dönüştürmesi
Diğer bir çözüm, verileri saklarken yaşam sürelerini dönüştürmek için güvensiz kod kullanmaktı; bu, motorun tüm referansları kaynak alan çıkmadan önce düştüğünü vaat ediyordu. Bu, istenen API ergonomisini sağlasa da, bellek güvenliği yükünü tamamen motor geliştiricilerine yüklemiş oluyordu. Referansların menşei takibindeki herhangi bir hata, sömürülebilir serbest bırakma güvenlik açıklarına yol açacaktı ve bu durum Rust'ın güvenlik garantilerini ihlal ederken, kod tabanını denetlemeyi zorlaştırıyordu.
Tüm eklenti dönüş değerlerinin 'static sınırı ile Any'yi uygulamasını gerektirmeye karar verdik; bu, betik yazarlarını sahipli veriler veya Arc ile sarılmış paylaşılan durumu döndürmeye zorladı. Bu karar, motorun etkinlik kuyruğunun verileri güvenli bir şekilde saklayıp işleyebileceği garantisi için sıfır kopya referanslarının bazı teorik performans faydalarından feragat etti. Sonuç, kamu arayüzünde hiçbir güvensiz kod bulunmayan sağlam bir eklenti API'si oldu; ancak, daha önce geçici borçlara dayanan türler için serileştirme katmanları eklememizi gerektirdi.
Neden Any sadece trait nesnesini oluşturmak için kullanılan referansın yaşam süresi yerine 'static gerektiriyor?
Any trait'i, bir vtbl üretmek için derleme zamanında tip bilgisini siler ve bu sırada tüm yaşam süresi verilerini kaybeder. &dyn Any oluşturduğunuzda, derleyici, trait nesnesine orijinal yaşam süresi 'a'yı daha sonra aşağıya indirme mekanizmasının doğrulayabileceği şekilde kodlayamaz. 'static gerektirmek, altta yatan tipin asılı işaretçileri içermediğini garanti etmenin tek yoludur; çalışma zamanında yaşam süresi takibi olmaksızın. Eğer Any daha kısa yaşam sürelerini kabul etseydi, vtbl işaretçisi yaşam süresi meta verilerini taşımak zorunda kalırdı; bu da Rust'ın bağımlı tipler veya çalışma zamanı borç kontrolü uygulamasını gerektirir, dilin sıfır maliyetli abstraksiyon modelini köklü bir şekilde değiştirirdi.
Box<dyn Any> nasıl, orijinal tip boş referanslar içerdiğinde 'static sınırıyla etkileşir?**
struct Wrapper<'a>(&'a str) gibi bir tip, 'static trait sınırını karşılamadığından Any'yi uygulayamaz. Sonuç olarak, Wrapper<'a> örneğinden Box<dyn Any> oluşturamazsınız. Adaylar, değeri kutulamanın yaşam süresini uzattığını yanlış bir şekilde düşünmektedir; ancak Box yalnızca yığında bulunan tahsisi sahiplenirken, bu tahsis içindeki alanlara referans veren verileri sahiplenmez. Eğer referans verilen veri yerel yığındaysa, dış yapıyı yığınlardan yığına taşımak, referansın yaşam süresini uzatmaz; bu nedenle, derleyici Box<dyn Any>'ye dönüştürmeyi doğru bir şekilde reddeder. Bu, yığındaki referansları içeren yığın çerçevesini aşan yığı tahsisi alan bir kutu durumunu önler.
Bir özel Any trait'ini, 'static gereksinimini gevşeterek güvenli olmayan kod ve manuel yaşam süresi takibi ile güvenli bir şekilde uygulayabilir misiniz?
Güvenilmeyen yaşam sürelerini ve özel vtbl'leri dönüştürmek için teknik olarak mümkün olsa da, bu tür bir uygulama geçersiz olacak, çünkü Rust'ın trait sistemi ve borç kontrolü, aşağıya indirme noktasında yaşam süresi sürekliliklerini doğrulayamaz. Çalışma zamanında yaşam sürelerini takip eden paralel bir tip sistemi uygulamanız ve her erişimde orijinal alanın hala var olup olmadığını kontrol etmeniz gerekecektir. Bu yaklaşım, esasen bir çöp toplayıcı veya referans sayma sistemini yeniden uygulamakta olup, Rust'ın derleme zamanı garantilerini kaybetmektedir. Ayrıca, herhangi bir güvensiz uygulama, Any sürekliliklerini bekleyen standart kütüphane bileşenleri ile uyumsuz etkileşime geçecek ve std::any::Any trait nesneleri ile karıştırıldığında tanımsız davranışa yol açacaktır.