Der Begriff "Fettzeiger" bezieht sich auf Referenzen und Rohzeiger auf Typen mit dynamischer Größe (DSTs) - Slices oder Trait-Objekte. Ein fetter Zeiger enthält einen Zeiger sowie einige Informationen, die die Sommerzeit "vollständig" machen (z. B. die Länge).
Die am häufigsten verwendeten Typen in Rust sind keine Sommerzeiten, sondern haben eine feste Größe, die zur Kompilierungszeit bekannt ist. Diese Typen implementieren das SizedMerkmal . Selbst Typen, die einen Heap-Puffer mit dynamischer Größe (wie Vec<T>) verwalten, Sizedwissen genau, wie viele Bytes eine Vec<T>Instanz auf dem Stapel benötigt. Derzeit gibt es in Rust vier verschiedene Arten von Sommerzeiten.
Scheiben ( [T]und str)
Der Typ [T](für jeden T) hat eine dynamische Größe (ebenso der spezielle Typ "String Slice" str). Deshalb sehen Sie es normalerweise nur als &[T]oder &mut [T], dh hinter einer Referenz. Diese Referenz ist ein sogenannter "Fettzeiger". Lass uns nachsehen:
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
Dies druckt (mit einigen Aufräumarbeiten):
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
Wir sehen also, dass ein Verweis auf einen normalen Typ wie u328 Byte groß ist, ebenso wie ein Verweis auf ein Array [u32; 2]. Diese beiden Typen sind keine Sommerzeiten. Aber wie [u32]bei einer Sommerzeit ist der Verweis darauf doppelt so groß. Im Fall von Slices sind die zusätzlichen Daten, die die Sommerzeit "vervollständigen", einfach die Länge. Man könnte also sagen, dass die Darstellung &[u32]ungefähr so ist:
struct SliceRef {
ptr: *const u32,
len: usize,
}
Merkmalsobjekte ( dyn Trait)
Bei Verwendung von Merkmalen als Merkmalsobjekte (dh Typ gelöscht, dynamisch versendet) sind diese Merkmalsobjekte Sommerzeiten. Beispiel:
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>());
Dies druckt (mit einigen Aufräumarbeiten):
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Auch hier &Catist nur 8 Bytes groß, da Cates sich um einen normalen Typ handelt. Ist dyn Animalaber ein Merkmalsobjekt und daher dynamisch dimensioniert. Als solches &dyn Animalist 16 Bytes groß.
Bei Merkmalsobjekten sind die zusätzlichen Daten, die die Sommerzeit vervollständigen, ein Zeiger auf die vtable (die vptr). Ich kann das Konzept von vtables und vptrs hier nicht vollständig erklären, aber sie werden verwendet, um die korrekte Methodenimplementierung in diesem virtuellen Versandkontext aufzurufen. Die vtable ist ein statisches Datenelement, das im Grunde nur einen Funktionszeiger für jede Methode enthält. Damit wird ein Verweis auf ein Merkmalsobjekt grundsätzlich dargestellt als:
struct TraitObjectRef {
data_ptr: *const (),
vptr: *const (),
}
(Dies unterscheidet sich von C ++, wo das vptr für abstrakte Klassen im Objekt gespeichert ist. Beide Ansätze haben Vor- und Nachteile.)
Benutzerdefinierte Sommerzeiten
Es ist tatsächlich möglich, eigene Sommerzeiten zu erstellen, indem Sie eine Struktur haben, in der das letzte Feld eine Sommerzeit ist. Dies ist jedoch eher selten. Ein prominentes Beispiel ist std::path::Path.
Ein Verweis oder Zeiger auf die benutzerdefinierte Sommerzeit ist auch ein fetter Zeiger. Die zusätzlichen Daten hängen von der Art der Sommerzeit innerhalb der Struktur ab.
Ausnahme: Externe Typen
In RFC 1861 wurde die extern typeFunktion eingeführt. Externe Typen sind ebenfalls Sommerzeiten, aber Zeiger auf sie sind keine fetten Zeiger. Oder genauer gesagt, wie der RFC es ausdrückt:
In Rust enthalten Zeiger auf Sommerzeiten Metadaten zu dem Objekt, auf das verwiesen wird. Für Strings und Slices ist dies die Länge des Puffers, für Trait-Objekte die vtable des Objekts. Für externe Typen sind die Metadaten einfach (). Dies bedeutet, dass ein Zeiger auf einen externen Typ dieselbe Größe wie a hat usize(dh es ist kein "fetter Zeiger").
Wenn Sie jedoch nicht mit einer C-Schnittstelle interagieren, müssen Sie sich wahrscheinlich nie mit diesen externen Typen befassen.
Oben haben wir die Größen für unveränderliche Referenzen gesehen. Fette Zeiger funktionieren genauso für veränderbare Referenzen, unveränderliche Rohzeiger und veränderbare Rohzeiger:
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16