Co to jest "gruby wskaźnik"?
Już w kilku kontekstach czytałem termin "fat pointer", 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.
1 answers
Termin" fat pointer " jest używany w odniesieniu do referencji i surowych wskaźników do typów o dynamicznym rozmiarze (DSTs) – plastrów lub obiektów cech. wskaźnik fat zawiera wskaźnik oraz pewne informacje, które sprawiają, że DST jest "kompletny" (np. długość).
Najczęściej używanymi typami w Rust są Nie DSTs, ale mają stały rozmiar znany w czasie kompilacji. Te typy implementują cechę Sized
. Nawet typy, które zarządzają buforem stosu o dynamicznej wielkości (jak Vec<T>
) są Sized
, ponieważ kompilator zna dokładną liczbę bajtów, jakie instancja Vec<T>
zajmie na stosie. Obecnie istnieją cztery różne rodzaje DST w Rust.
Plastry ([T]
i str
)
Typ [T]
(dla dowolnego T
) ma dynamiczny rozmiar(tak jak specjalny typ "string slice" str
). Dlatego zwykle widzisz go tylko jako &[T]
lub &mut [T]
, tzn. 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 jakimś 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 rozmiar 8 bajtów, podobnie jak odniesienie do tablicy [u32; 2]
. Te dwa typy nie są DSTs. Ale ponieważ [u32]
jest DST, odniesienie do niego jest dwa razy większe. W przypadku plasterków, dodatkowe dane, które "uzupełnia" DST jest po prostu długość. można więc powiedzieć, że reprezentacja &[u32]
jest czymś takim:
struct SliceRef {
ptr: *const u32,
len: usize,
}
Obiekty cechowe (dyn Trait
)
Przy użyciu cech jako obiekty trait (tzn. Typ kasowany, dynamicznie wysyłany), te obiekty trait są DSTs. 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 jakimś 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 cechowym i dlatego jest dynamicznie skalowany. &dyn Animal
ma rozmiar 16 bajtów.
W przypadku obiektów trait, dodatkowe dane, które uzupełniają DST, są wskaźnikiem do vtable (vptr). nie mogę w pełni wyjaśnić pojęcie vtables i vptrs tutaj, ale są one używane do wywołania prawidłowej implementacji metody w tym wirtualnym kontekście wysyłki. Vtable jest statycznym kawałkiem danych, który zasadniczo zawiera tylko wskaźnik funkcji dla każdej metody. Z tego powodu, 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 wewnątrz obiektu. Oba podejścia mają zalety i wady.)
Niestandardowe DSTs
Możliwe jest tworzenie własnych DST poprzez strukturę, w której ostatnim polem jest DST. Jest to jednak raczej rzadkie. Jednym z najważniejszych przykładów jest std::path::Path
.
Odniesienie lub wskaźnik do niestandardowego DST jest również wskaźnikiem fat. Dodatkowe dane zależą od rodzaju DST wewnątrz struktury.
Exception: Extern types
W RFC 1861 , funkcja extern type
została wprowadzona. Typy Extern są również DSTs, ale wskaźniki do nich są Nie grube wskaźniki. A dokładniej, jak to określa 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 trait jest to vtable obiektu. Dla typów extern metadane są po prostu
()
. Oznacza to, że wskaźnik do typu extern ma taki sam rozmiar jakusize
(tj. nie jest "grubym wskaźnikiem").
Ale jeśli nie wchodzicie w interakcję z interfejsem C, prawdopodobnie nigdy nie będzie miał do czynienia z tymi zewnętrznymi typami.
Powyżej widzieliśmy rozmiary niezmiennych odniesień. Wskaźniki Fat działają tak samo dla zmiennych odniesień, niezmiennych wskaźników raw i zmiennych wskaźników raw: {]}
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55