Saya telah membaca istilah "penunjuk lemak" dalam beberapa konteks, tapi saya tidak yakin apa sebenarnya artinya dan kapan digunakan di Rust. Penunjuk tampaknya dua kali lebih besar dari penunjuk normal, tapi saya tidak mengerti mengapa. Ini juga tampaknya ada hubungannya dengan objek sifat.
91
Jawaban:
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 (sepertiVec<T>
) adalahSized
karena kompilator mengetahui jumlah pasti byte yangVec<T>
akan digunakan instance di stack. Saat ini ada empat jenis DST berbeda di Rust.Irisan (
[T]
danstr
)Jenis
[T]
(untuk apa sajaT
) berukuran dinamis (begitu juga jenis "potongan tali" khususstr
). 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):
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):
Sekali lagi, ukurannya
&Cat
hanya 8 byte karenaCat
merupakan tipe normal. Tetapidyn 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: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
sumber