Czytałem już termin „gruby wskaźnik” w kilku kontekstach, ale nie jestem pewien, co dokładnie oznacza i kiedy jest używany w Rust. Wskaźnik wydaje się być dwa razy większy niż normalny wskaźnik, ale nie rozumiem dlaczego. Wydaje się również, że ma to coś wspólnego z obiektami cech.
91
Odpowiedzi:
Termin „gruby wskaźnik” jest używany w odniesieniu do odniesień i surowych wskaźników do typów o dynamicznej wielkości (DST) - plasterków lub obiektów cech. Gruby wskaźnik zawiera wskaźnik plus pewne informacje, które sprawiają, że DST jest „kompletny” (np. Długość).
Najczęściej używanymi typami w Rust nie są DST, ale mają one stały rozmiar znany w czasie kompilacji. Te typy wdrożyć ten
Sized
cechę . Nawet typy, które zarządzają buforem sterty o rozmiarze dynamicznym (takim jakVec<T>
), sąSized
takie, jakie kompilator zna dokładną liczbę bajtów, któreVec<T>
instancja zajmie na stosie. Obecnie w Rust istnieją cztery różne rodzaje czasu letniego.Plasterki (
[T]
istr
)Typ
[T]
(dla dowolnegoT
) jest określany dynamicznie (tak samo jak specjalny typ „wycinka łańcucha”str
). Dlatego zwykle widzisz to tylko jako&[T]
lub&mut [T]
, tj. Za odniesieniem. To odniesienie jest tak zwanym „wskaźnikiem tłuszczu”. Sprawdźmy:dbg!(size_of::<&u32>()); dbg!(size_of::<&[u32; 2]>()); dbg!(size_of::<&[u32]>());
To drukuje (z pewnym porządkiem):
Widzimy więc, że odniesienie do normalnego typu, takiego jak,
u32
ma 8 bajtów, podobnie jak odniesienie do tablicy[u32; 2]
. Te dwa typy nie są DST. Ale tak jak w[u32]
przypadku czasu letniego, odniesienie do niego jest dwukrotnie większe. W przypadku wycinków dodatkowe dane, które „uzupełniają” czas letni, to po prostu długość. Można więc powiedzieć, że reprezentacja&[u32]
wygląda mniej więcej tak:struct SliceRef { ptr: *const u32, len: usize, }
Obiekty cech (
dyn Trait
)W przypadku używania cech jako obiektów cech (tj. Wymazywania typu, dynamicznego wysyłania), te obiekty cech są DST. Przykład:
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>());
To drukuje (z pewnym porządkiem):
Ponownie,
&Cat
ma tylko 8 bajtów, ponieważCat
jest typem normalnym. Aledyn Animal
jest obiektem cech, a zatem ma dynamiczne rozmiary. W związku z tym&dyn Animal
ma rozmiar 16 bajtów.W przypadku obiektów cech, dodatkowymi danymi, które uzupełniają DST, jest wskaźnik do tabeli vtable (vptr). Nie mogę w pełni wyjaśnić tutaj koncepcji vtables i vptrs, ale są one używane do wywoływania poprawnej implementacji metody w tym kontekście wirtualnej wysyłki. Tabela vtable to statyczny fragment danych, który w zasadzie zawiera tylko wskaźnik funkcji dla każdej metody. W związku z tym odniesienie do obiektu cechy jest zasadniczo reprezentowane jako:
struct TraitObjectRef { data_ptr: *const (), vptr: *const (), }
(Różni się to od C ++, gdzie vptr dla klas abstrakcyjnych jest przechowywany w obiekcie. Oba podejścia mają zalety i wady).
Niestandardowe DST
W rzeczywistości możliwe jest utworzenie własnego czasu letniego, mając strukturę, w której ostatnim polem jest czas letni. Jest to jednak raczej rzadkie. Jednym z wybitnych przykładów jest
std::path::Path
.Odniesienie lub wskaźnik do niestandardowego czasu letniego jest również grubym wskaźnikiem. Dodatkowe dane zależą od rodzaju czasu letniego wewnątrz struktury.
Wyjątek: typy zewnętrzne
W dokumencie RFC 1861 ,
extern type
funkcja została wprowadzona. Typy zewnętrzne to także DST, ale wskaźniki do nich nie są grubymi wskaźnikami. A dokładniej, jak to ujmuje RFC:Ale jeśli nie korzystasz z interfejsu C, prawdopodobnie nigdy nie będziesz musiał radzić sobie z tymi zewnętrznymi typami.
Powyżej widzieliśmy rozmiary niezmiennych odwołań. Grubsze wskaźniki działają tak samo dla zmiennych odwołań, niezmiennych wskaźników surowych i zmiennych wskaźników surowych:
size_of::<&[u32]>() = 16 size_of::<&mut [u32]>() = 16 size_of::<*const [u32]>() = 16 size_of::<*mut [u32]>() = 16
źródło