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.

Author: vallentin, 2019-09-02

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 jak usize (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
 115
Author: Lukas Kalbertodt,
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