Ich habe den Begriff "Fettzeiger" bereits in mehreren Zusammenhängen gelesen, bin mir aber nicht sicher, was er genau bedeutet und wann er in Rust verwendet wird. Der Zeiger scheint doppelt so groß zu sein wie ein normaler Zeiger, aber ich verstehe nicht warum. Es scheint auch etwas mit Merkmalsobjekten zu tun zu haben.
91
Antworten:
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
Sized
Merkmal . Selbst Typen, die einen Heap-Puffer mit dynamischer Größe (wieVec<T>
) verwalten,Sized
wissen genau, wie viele Bytes eineVec<T>
Instanz auf dem Stapel benötigt. Derzeit gibt es in Rust vier verschiedene Arten von Sommerzeiten.Scheiben (
[T]
undstr
)Der Typ
[T]
(für jedenT
) 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):
Wir sehen also, dass ein Verweis auf einen normalen Typ wie
u32
8 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):
Auch hier
&Cat
ist nur 8 Bytes groß, daCat
es sich um einen normalen Typ handelt. Istdyn Animal
aber ein Merkmalsobjekt und daher dynamisch dimensioniert. Als solches&dyn Animal
ist 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 type
Funktion eingeführt. Externe Typen sind ebenfalls Sommerzeiten, aber Zeiger auf sie sind keine fetten Zeiger. Oder genauer gesagt, wie der RFC es ausdrückt: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
quelle