Istilah "penunjuk lemak" digunakan untuk merujuk pada referensi dan penunjuk mentah untuk tipe berukuran dinamis (DST) - irisan atau objek sifat. Penunjuk gemuk berisi penunjuk ditambah beberapa informasi yang membuat DST "lengkap" (misalnya panjangnya).
Jenis yang paling umum digunakan di Rust bukanlah DST, tetapi memiliki ukuran tetap yang diketahui pada waktu kompilasi. Jenis menerapkan yang Sized
sifat . Bahkan jenis yang mengelola buffer heap dengan ukuran dinamis (seperti Vec<T>
) adalah Sized
karena kompilator mengetahui jumlah pasti byte yang Vec<T>
akan digunakan instance di stack. Saat ini ada empat jenis DST berbeda di Rust.
Irisan ( [T]
dan str
)
Jenis [T]
(untuk apa saja T
) berukuran dinamis (begitu juga jenis "potongan tali" khusus str
). Itulah mengapa Anda biasanya hanya melihatnya sebagai &[T]
atau &mut [T]
, yaitu di belakang referensi. Referensi ini disebut "penunjuk lemak". Mari kita periksa:
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
Ini mencetak (dengan beberapa pembersihan):
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
Jadi kita melihat bahwa referensi ke tipe normal seperti u32
berukuran 8 byte, seperti referensi ke array [u32; 2]
. Kedua tipe tersebut bukanlah DST. Tetapi seperti halnya [u32]
DST, rujukannya dua kali lebih besar. Dalam kasus irisan, data tambahan yang "melengkapi" DST hanyalah panjangnya. Jadi dapat dikatakan representasi dari &[u32]
sesuatu seperti ini:
struct SliceRef {
ptr: *const u32,
len: usize,
}
Objek sifat ( dyn Trait
)
Saat menggunakan sifat sebagai objek sifat (yaitu tipe terhapus, dikirim secara dinamis), objek sifat ini adalah DST. Contoh:
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>());
Ini mencetak (dengan beberapa pembersihan):
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Sekali lagi, ukurannya &Cat
hanya 8 byte karena Cat
merupakan tipe normal. Tetapi dyn Animal
merupakan ciri objek dan karena itu berukuran dinamis. Dengan demikian, &dyn Animal
ukurannya adalah 16 byte.
Dalam kasus objek sifat, data tambahan yang melengkapi DST adalah penunjuk ke vtable (vptr). Saya tidak dapat sepenuhnya menjelaskan konsep vtables dan vptrs di sini, tetapi mereka digunakan untuk memanggil implementasi metode yang benar dalam konteks pengiriman virtual ini. Vtable adalah bagian data statis yang pada dasarnya hanya berisi penunjuk fungsi untuk setiap metode. Dengan itu, referensi ke objek sifat pada dasarnya direpresentasikan sebagai:
struct TraitObjectRef {
data_ptr: *const (),
vptr: *const (),
}
(Ini berbeda dari C ++, di mana vptr untuk kelas abstrak disimpan di dalam objek. Kedua pendekatan tersebut memiliki kelebihan dan kekurangan.)
DST Kustom
Sebenarnya dimungkinkan untuk membuat DST Anda sendiri dengan memiliki struct di mana bidang terakhir adalah DST. Ini agak jarang. Salah satu contoh yang menonjol adalah std::path::Path
.
Referensi atau pointer ke DST kustom juga merupakan pointer gemuk. Data tambahan tergantung pada jenis DST di dalam struct.
Pengecualian: Tipe eksternal
Di RFC 1861 , extern type
fitur tersebut diperkenalkan. Jenis eksternal juga merupakan DST, tetapi petunjuk ke sana bukanlah penunjuk lemak. Atau lebih tepatnya, seperti yang dikatakan RFC:
Di Rust, pointer ke DST membawa metadata tentang objek yang diarahkan. Untuk string dan irisan ini adalah panjang buffer, untuk objek ciri ini adalah vtable objek. Untuk tipe eksternal, metadatanya sederhana ()
. Ini berarti bahwa penunjuk ke tipe eksternal memiliki ukuran yang sama dengan a usize
(yaitu, bukan "penunjuk lemak").
Tetapi jika Anda tidak berinteraksi dengan antarmuka C, Anda mungkin tidak perlu berurusan dengan tipe eksternal ini.
Di atas, kami telah melihat ukuran untuk referensi yang tidak dapat diubah. Pointer gemuk berfungsi sama untuk referensi yang dapat diubah, pointer mentah yang tidak dapat diubah, dan pointer mentah yang dapat diubah:
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16