Термин «толстый указатель» используется для обозначения ссылок и необработанных указателей на типы с динамическим размером (DST) - срезы или объекты-признаки. Толстый указатель содержит указатель плюс некоторую информацию, которая делает DST «полным» (например, длину).
Наиболее часто используемые типы в Rust не являются DST, но имеют фиксированный размер, известный во время компиляции. Эти типы реализации на Sized
черту . Даже типы, которые управляют буфером кучи динамического размера (например, Vec<T>
), таковы, Sized
поскольку компилятор знает точное количество байтов, которое Vec<T>
экземпляр займет в стеке. В настоящее время в Rust есть четыре различных типа DST.
Ломтики ( [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]
в случае с DST, ссылки на него в два раза больше. В случае срезов дополнительными данными, которые «завершают» DST, является просто длина. Можно сказать, что представление &[u32]
выглядит примерно так:
struct SliceRef {
ptr: *const u32,
len: usize,
}
Объекты трейта ( dyn Trait
)
При использовании признаков в качестве объектов признаков (т. Е. Стертый тип, динамическая отправка) эти объекты признака являются DST. Пример:
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 байт.
В случае типажных объектов дополнительные данные, которые завершают DST, являются указателем на vtable (vptr). Я не могу полностью объяснить концепцию vtables и vptrs здесь, но они используются для вызова правильной реализации метода в этом контексте виртуальной диспетчеризации. Таблица vtable - это статическая часть данных, которая в основном содержит только указатель на функцию для каждого метода. При этом ссылка на объект-признак в основном представлена как:
struct TraitObjectRef {
data_ptr: *const (),
vptr: *const (),
}
(Это отличается от C ++, где vptr для абстрактных классов хранится внутри объекта. Оба подхода имеют свои преимущества и недостатки.)
Пользовательские DST
На самом деле можно создавать свои собственные DST, имея структуру, в которой последнее поле является DST. Однако это довольно редко. Один из ярких примеров std::path::Path
.
Ссылка или указатель на настраиваемое летнее время также является жирным указателем. Дополнительные данные зависят от типа DST внутри структуры.
Исключение: внешние типы
В RFC 1861 эта extern type
функция была представлена. Типы Extern также являются DST, но указатели на них не являются жирными указателями. Или, точнее, как сказано в 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