"Yağ işaretçisi" terimi, dinamik olarak boyutlandırılmış türlere (DST'ler) - dilimler veya özellik nesneleri - referansları ve ham işaretçileri ifade etmek için kullanılır . Bir şişman işaretçi, bir işaretçi artı DST'yi "tamamlayan" (örneğin uzunluk) bazı bilgiler içerir.
Rust'ta en yaygın olarak kullanılan türler DST değildir , ancak derleme zamanında bilinen sabit bir boyuta sahiptir. Bu tipler uygulamak özelliği . Dinamik boyuttaki bir yığın arabelleğini (gibi ) yöneten türler bile , derleyicinin bir örneğin yığın üzerinde alacağı tam bayt sayısını bildiği gibidir . Rust'ta şu anda dört farklı DST türü vardır.Sized
Vec<T>
Sized
Vec<T>
Dilimler ( [T]
ve str
)
Tür [T]
(herhangi T
biri için ) dinamik olarak boyutlandırılmıştır (özel "dize dilimi" türü de öyledir str
). Bu yüzden onu genellikle sadece &[T]
veya &mut [T]
yani bir referansın arkasında görüyorsunuz . Bu referans, sözde "şişman gösterici" dir. Hadi kontrol edelim:
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
Bu (biraz temizleme ile) yazdırır:
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
Böylece u32
, bir diziye referans olduğu gibi normal bir türe yapılan bir referansın 8 bayt büyüklüğünde olduğunu görüyoruz [u32; 2]
. Bu iki tür DST değildir. Ancak [u32]
DST olduğu gibi , ona yapılan referans iki kat daha büyüktür. Dilimler söz konusu olduğunda, DST'yi "tamamlayan" ek veriler yalnızca uzunluktur. Dolayısıyla temsili &[u32]
şunun gibi bir şey diyebiliriz :
struct SliceRef {
ptr: *const u32,
len: usize,
}
Özellik nesneleri ( dyn Trait
)
Nitelikleri özellik nesneleri olarak kullanırken (yani silinen, dinamik olarak gönderilen türler), bu özellik nesneleri DST'lerdir. Misal:
trait Animal {
fn speak(&self);
}
struct Cat;
impl Animal for Cat {
fn speak(&self) {
println!("meow");
}
}
dbg!(size_of::<&Cat>());
dbg!(size_of::<&dyn Animal>());
Bu (biraz temizleme ile) yazdırır:
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Yine, &Cat
sadece 8 bayt büyüklüğündedir çünkü Cat
normal bir türdür. Ancak dyn Animal
özel bir nesnedir ve bu nedenle dinamik olarak boyutlandırılır. Bu nedenle &dyn Animal
16 bayt büyüklüğündedir.
Özellik nesneleri durumunda, DST'yi tamamlayan ek veriler, vtable'a (vptr) bir göstericidir. Burada vtables ve vptr kavramlarını tam olarak açıklayamam, ancak bunlar bu sanal gönderim bağlamında doğru yöntem uygulamasını çağırmak için kullanılır. Vtable, temelde her yöntem için yalnızca bir işlev işaretçisi içeren statik bir veri parçasıdır. Bununla birlikte, bir özellik nesnesine yapılan referans temelde şu şekilde temsil edilir:
struct TraitObjectRef {
data_ptr: *const (),
vptr: *const (),
}
(Bu, soyut sınıflar için vptr'nin nesne içinde depolandığı C ++ 'dan farklıdır. Her iki yaklaşımın da avantajları ve dezavantajları vardır.)
Özel DST'ler
Son alanın DST olduğu bir yapıya sahip olarak kendi DST'lerinizi oluşturmak aslında mümkündür. Yine de bu oldukça nadirdir. Öne çıkan bir örnek std::path::Path
.
Özel DST'ye bir referans veya işaretçi de bir şişman işaretçidir. Ek veriler, yapı içindeki DST türüne bağlıdır.
İstisna: İstisna türleri
In RFC 1861 , extern type
özelliği getirildi. Harici türler de DST'lerdir, ancak onlara yönelik işaretler şişman işaretçiler değildir . Veya daha doğrusu, RFC'nin dediği gibi:
Rust'ta, DST'lerin işaretçileri, işaret edilen nesne hakkındaki meta verileri taşır. Dizeler ve dilimler için bu, tamponun uzunluğudur, özellik nesneleri için bu, nesnenin vtable'sidir. Harici türleri için meta veriler basittir ()
. Bu, bir harici tipin göstericisinin a ile aynı boyuta sahip olduğu anlamına gelir usize
(yani, "şişman işaretçi" değildir).
Ancak bir C arayüzü ile etkileşim kurmuyorsanız, muhtemelen bu harici tiplerle uğraşmak zorunda kalmayacaksınız.
Yukarıda, değişmez referanslar için boyutları gördük. Şişman işaretçiler, değiştirilebilir referanslar, değişmez ham işaretçiler ve değiştirilebilir ham işaretçiler için aynı şekilde çalışır:
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16