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 jak Vec<T>
), są Sized
takie, jakie kompilator zna dokładną liczbę bajtów, które Vec<T>
instancja zajmie na stosie. Obecnie w Rust istnieją cztery różne rodzaje czasu letniego.
Plasterki ( [T]
i str
)
Typ [T]
(dla dowolnego T
) 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):
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
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):
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Ponownie, &Cat
ma tylko 8 bajtów, ponieważ Cat
jest typem normalnym. Ale dyn 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:
W Rust wskaźniki do DST zawierają metadane dotyczące wskazywanego obiektu. Dla łańcuchów i plasterków jest to długość bufora, dla obiektów cech jest to vtable obiektu. W przypadku typów zewnętrznych metadane są po prostu ()
. Oznacza to, że wskaźnik do typu zewnętrznego ma taki sam rozmiar jak a usize
(tj. Nie jest „grubym wskaźnikiem”).
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