Rust, yapıların içindeki alanlar veya dizilerin içindeki elemanlar için kullanılan tüm türlerin Sized trait'ini uygulamasını zorunlu kılar, böylece derleyici, sabit bellek offsetlerini ve yığın çerçeve düzenlerini derleme zamanı hesaplayabilir. dyn Trait yapısı, dinamik olarak yönlendirilmiş bir trait nesnesini temsil eder ve arayüzün arkasındaki somut tür silindiği için doğal olarak !Sized (boyutsuzdur). Bu, farklı bellek ayak izlerine sahip çeşitli uygulamaların aynı soyut türü kaplamasına olanak tanır. Dinamik yönlendirmeyi kolaylaştırmak için Rust, dyn Trait'i iki kelime yapısından oluşan şişman işaretçi olarak temsil eder; bir veri işaretçisi ve yöntem adresleri ile yok edici bilgilerini saklayan bir vtable işaretçisi içerir. Ancak, tür kendisi boyutsuz kalır, çünkü işaretçi elemanının boyutu bilinmemektedir. Sonuç olarak, dyn Trait'in doğrudan yerleştirilmesi Sized sınırını ihlal eder, çünkü derleyici yapı sınırlarını veya dizi atlamasını belirleyemez; işaretçiyi Box, Rc, Arc veya referanslar & ile sarmak gerekmektedir.
Bir oyun motoru için eklenti mimarisi tasarlıyorsunuz ve mod yapıcılar, bazıları basit tam sayı bayrakları depolayan, diğerleri büyük mekansal hash ızgaralarını koruyan çeşitli uygulamalar sağlar ve motor, GameState yapısında aktif davranışların bir koleksiyonunu korumalıdır.
struct GameState { behaviors: Vec<dyn Behavior> } tanımlamaya çalışmak derhal derleme aşamasında hata ile sonuçlanır; çünkü dyn Behavior derleme zamanı ile bilinen sabit bir boyuta sahip değildir, bu durum derlemeyi engeller.
Düşünülen çözümlerden biri, işaretçilerin kendileri için yığın tahsisini önleyerek borçlu trait nesnelerini depolamak için Vec<&dyn Behavior> kullanmaktı. Bu yaklaşım, tüm eklenti verilerinin en az GameState kadar uzun ömürlü olmasını gerektiren ciddi yaşam süresi kısıtlamaları getiriyor ve dinamik olarak boşaltılan eklentilerin sıcak yeniden yükleme senaryolarında karmaşıklaşıyor; bu durum, modifiye edilebilir bir motor için fazla kısıtlayıcı olduğunu kanıtladı.
Değerlendirilen bir diğer alternatif, bilinen tüm uygulamaları saran enum BehaviorType { Ai(AiModule), Physics(PhysicsBody) } tanımlayarak enum yönlendirmesiydi. Bu, statik yönlendirme ve mükemmel önbellek yerelitesi sağlarken, her yeni eklenti için ana motoru değiştirerek kapalı bir küme oluşturuyordu, bu durum açık/kapalı ilkesini ihlal ediyordu ve üçüncü taraf ikili uzantıları yeniden derleme olmadan engelliyordu.
Seçilen çözüm, her davranış örneğini yığın tahsis eden ve sonuçta oluşan şişman işaretçileri vektörde depolayan Vec<Box<dyn Behavior>> kullandı. Bu, Box dolaylılığı aracılığıyla Sized gereksinimini karşıladı, aynı zamanda çalışma zamanı çeşitliliğini koruyarak ve heterojen koleksiyonlara izin vererek, küçük davranış bileşenleri için özel bir arenayapıcı ile azaltılan öngörülebilir yığın parçalanma maliyetlerini tanıttı.
CoerceUnsized, Box<T>'yi Box<dyn Trait>'e dönüştürürken yeni bir vtable'yi çalışma zamanında tahsis etmeden nasıl kolaylaştırır, ve bu durum pointee üzerinde hangi bellek düzeni kısıtlamaları getirir?
CoerceUnsized, Box, Rc ve Arc gibi akıllı işaretçiler tarafından uygulanmış bir işaretçi trait'idir ve boyutsuz zorlamalara izin verir. Box<Concrete>'yi Box<dyn Trait>'e dönüştürürken, derleyici, Trait'i uygulayan Concrete için vtable'yi derleme sırasında statik olarak oluşturur ve bunu ikili dosyanın salt okunur bölümüne yerleştirir. Zorlamak yalnızca işaretçi meta verisini yeniden yorumlayarak, ince işaretçiden (tek kelime) şişman işaretçiye (veri adresi + vtable adresi) genişletir, böylece altta yatan veri taşınmadan veya çalışma zamanında bellek tahsisi yapılmadan bunun üstesinden gelir. Bu, somut türün, trait nesnesinin beklenen temsilinin uyumlu bir bellek düzenine sahip olmasını zorunlu kılar; özellikle, veri işaretçisi, vtable'ın alanları beklediği nesnenin başlangıcıyla hizalanmalıdır ve tür, #[repr(Rust)] veya uyumlu temsil garantilerini dikkatlice sağlamalıdır; bu, vtable'deki yöntem offsetlerinin somut uygulamanın işlevlerine doğru şekilde bağlanmasını sağlayarak, güvence altına alır.
Rust, değeri tüketen (fn consume(self)) yöntemler tanımlayan traitlerden nesne nesneleri (dyn Trait) yaratmayı neden yasaklar ve bu durum işlev dönüş türleri için Sized gereksinimi ile nasıl ilişkilidir?
Bu yasak, nesne güvenliği kurallarından kaynaklanır. Bir yöntem, self'i değer ile tükettğinde, derleyici, değeri taşımak için uygun yığın çerçevesini oluşturmak ve doğru yok edici çağrısını tam bellek offsetinde yerleştirmek için somut türün kesin boyutunu bilmek zorundadır. dyn Trait bağlamında, somut tür silinmiştir; vtable, boyut ve yok etme bilgilerini içerse de, çağıranın yığın çerçevesi, taşınan değerin bilinmeyen boyutunu karşılayacak şekilde dinamik olarak ayarlanamaz. Ayrıca, Self'i döndüren yöntemler, çağıranın bilinmeyen boyut için döndürme alanı tahsis etmesini gerektirir. Yığın bozulmasını ve tanımsız davranışı önlemek için Rust, değer ile self yöntemleri için trait nesnelerini yasaklar, tüm etkileşimlerin dolaylılık aracılığıyla gerçekleşmesini sağlayarak (&self veya &mut self) işaretçi boyutunun sabit olduğu durumlarda.
Trait nesnesinin Send olarak otomatik olarak uygulanması ile Trait’in Send'i süpertrait olarak taşıması arasındaki ayrım nedir ve neden her ikisinin yokluğu, arka plandaki somut tür Send’i uyguladığı halde trait nesnesinin thread-güvenliği kontrollerinden geçememesine neden olur?
Trait, Send'i süpertrait olarak tanımladığında (örneğin, trait Trait: Send {}), derleyici bu kısıtı yayar, dyn Trait için otomatik olarak Send'i uygular çünkü herhangi bir uygulayıcının mutlaka Send olması gerekir. Aksine, Trait bu süpertrait'i taşımıyorsa, dyn Trait + Send yazmak, yalnızca hem Trait hem de Send'i uygulayan somut türleri kabul eden bir trait nesnesi oluşturur; bu da zorlamanın yapıldığı yerde kabul edilebilir türleri daraltır. Ne süpertrait ne de açık bir kısıt varsa, dyn Trait Send'i uygulamaz, arka plandaki somut örnek iş parçacığı güvenli olsa bile, çünkü tür silinmesi bu bilgiyi yok eder—derleyici, işaretçi bloğunu dolduran tüm olası türlerin Send olduğunu garanti edemez. Bu durum, tür nesnesi tür silinmesi aracılığıyla iş parçacığı sınırları arasında güvenli olmayan türlerin kazara iletimini önler.