Термін "покажчик жиру" використовується для позначення посилань та необроблених покажчиків на типи динамічного розміру (DST) - фрагменти або об'єкти ознак. Покажчик жиру містить покажчик плюс деяку інформацію, яка робить літній час "повним" (наприклад, довжина).
Найчастіше використовувані типи в Rust не є літнім часом, але мають фіксований розмір, відомий під час компіляції. Ці типи реалізації на Sized
межу . Навіть типи, які керують буфером купи динамічного розміру (наприклад Vec<T>
), такі Sized
як компілятор знає точну кількість байт, яку Vec<T>
екземпляр займе у стеку. В даний час в Русті існує чотири різні типи літнього часу.
Фрагменти ( [T]
і str
)
Тип [T]
(для будь-якого T
) має динамічний розмір (так само, як і спеціальний тип "зріз рядка" str
). Ось чому ви зазвичай бачите це лише як &[T]
або &mut [T]
, тобто за посиланням. Це посилання є так званим "жировим покажчиком". Давайте перевіримо:
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
Це друкує (з деяким очищенням):
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
Отже, ми бачимо, що посилання на звичайний тип, наприклад, u32
має 8 байт, як і посилання на масив [u32; 2]
. Ці два типи не є літнім часом. Але як [u32]
і на літній час, посилання на нього вдвічі більше. У випадку зрізів додатковими даними, які "завершують" літній час, є просто довжина. Тож можна сказати, що представлення &[u32]
приблизно такого:
struct SliceRef {
ptr: *const u32,
len: usize,
}
Об'єкти ознак ( dyn Trait
)
При використанні ознак як об'єктів ознак (тобто тип стирається, динамічно відправляється), ці об'єкти ознак є датами часу. Приклад:
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>());
Це друкує (з деяким очищенням):
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Знову ж таки, &Cat
має лише 8 байт, оскільки Cat
це нормальний тип. Але dyn Animal
є об’єктом ознаки і тому має динамічний розмір. Таким чином, він &dyn Animal
має 16 байт.
У випадку об'єктів ознак додатковими даними, що заповнюють літній час, є вказівник на vtable (vptr). Я не можу повністю пояснити поняття vtables і vptrs тут, але вони використовуються для виклику правильної реалізації методу у цьому контексті віртуальної диспетчеризації. Vtable - це статична частина даних, яка в основному містить лише покажчик функції для кожного методу. При цьому посилання на об'єкт ознаки в основному представляється як:
struct TraitObjectRef {
data_ptr: *const (),
vptr: *const (),
}
(Це відрізняється від С ++, де vptr для абстрактних класів зберігається в об'єкті. Обидва підходи мають переваги та недоліки.)
Спеціальні переходи на літній час
Насправді можна створити власні датські часові пояси, маючи структуру, де останнє поле - літній час. Однак це досить рідко. Одним з яскравих прикладів є std::path::Path
.
Посилання або вказівник на власний перехід на літній час також є показником жиру. Додаткові дані залежать від виду літнього часу в структурі.
Виняток: зовнішні типи
У RFC 1861 ця extern type
функція була введена. Зовнішні типи - це також літній час, але вказівники на них не є показником жиру. Або точніше, як висловлюється RFC:
У Rust вказівники на DST містять метадані про об’єкт, на який вказують. Для рядків та фрагментів це довжина буфера, для об’єктів ознак це vtable об’єкта. Для зовнішніх типів метадані просто ()
. Це означає, що покажчик на тип extern має такий самий розмір, як і usize
(тобто він не є "жировим покажчиком").
Але якщо ви не взаємодієте з інтерфейсом C, вам, мабуть, ніколи не доведеться мати справу з цими зовнішніми типами.
Вище ми бачили розміри незмінних посилань. Покажчики жиру працюють однаково для змінних посилань, незмінних необроблених покажчиків та змінних необроблених покажчиків:
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16