J'ai déjà lu le terme "fat pointer" dans plusieurs contextes, mais je ne sais pas exactement ce qu'il signifie et quand il est utilisé dans Rust. Le pointeur semble être deux fois plus grand qu'un pointeur normal, mais je ne comprends pas pourquoi. Il semble également que cela ait quelque chose à voir avec les objets traits.
Réponse
Trop de publicités?Le terme "gros pointeur" est utilisé pour faire référence aux références et aux pointeurs bruts de les types à taille dynamique (DST) - tranches ou objets de traits. Un pointeur gras contient un pointeur plus quelques informations qui rendent le DST "complet" (par exemple, la longueur).
Les types les plus couramment utilisés dans Rust sont no DST, mais ont une taille fixe connue au moment de la compilation. Ces types mettent en œuvre le site Sized
trait . Même les types qui gèrent un tampon de tas de taille dynamique (tels que Vec<T>
) sont Sized
car le compilateur connaît le nombre exact d'octets qu'une Vec<T>
prendra place sur la pile. Il existe actuellement quatre types différents de DSTs en Rust.
Tranches ( [T]
y str
)
Le type [T]
(pour tout T
) est dimensionné dynamiquement (de même que le type spécial "string slice" (tranche de chaîne de caractères) str
). C'est pourquoi vous ne le voyez généralement que sous la forme de &[T]
o &mut [T]
c'est-à-dire derrière une référence. Cette référence est ce qu'on appelle un "gros pointeur". Vérifions :
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
Cela s'imprime (avec un peu de nettoyage) :
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
Nous voyons donc qu'une référence à un type normal comme u32
a une taille de 8 octets, tout comme une référence à un tableau [u32; 2]
. Ces deux types ne sont pas des DST. Mais comme [u32]
est un DST, la référence à celui-ci est deux fois plus grande. Dans le cas des tranches, les données supplémentaires qui "complètent" le DST sont simplement la longueur. On pourrait donc dire que la représentation de &[u32]
est quelque chose comme ça :
struct SliceRef {
ptr: *const u32,
len: usize,
}
Les objets Trait ( dyn Trait
)
Lorsque l'on utilise des traits en tant qu'objets traits (c'est-à-dire type effacé, distribué dynamiquement), ces objets traits sont des DST. Exemple :
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>());
Cela s'imprime (avec un peu de nettoyage) :
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Encore une fois, &Cat
n'a qu'une taille de 8 octets car Cat
est un type normal. Mais dyn Animal
est un objet trait et est donc dynamiquement dimensionné. En tant que tel, &dyn Animal
a une taille de 16 octets.
Dans le cas des objets traits, les données supplémentaires qui complètent le DST sont un pointeur vers la vtable (le vptr). Je ne peux pas expliquer complètement le concept des vtables et vptrs ici, mais ils sont utilisés pour appeler l'implémentation correcte de la méthode dans ce contexte de distribution virtuelle. La vtable est un élément de données statique qui contient essentiellement un pointeur de fonction pour chaque méthode. Avec cela, une référence à un objet trait est essentiellement représentée comme :
struct TraitObjectRef {
data_ptr: *const (),
vptr: *const (),
}
(Ceci est différent du C++, où le vptr des classes abstraites est stocké dans l'objet. Les deux approches ont des avantages et des inconvénients).
DST personnalisés
Il est en fait possible de créer vos propres DST en ayant une structure dont le dernier champ est un DST. C'est plutôt rare, cependant. Un exemple frappant est std::path::Path
.
Une référence ou un pointeur vers le DST personnalisé est également un pointeur gras. Les données supplémentaires dépendent du type de DST à l'intérieur de la structure.
Exception : Types externes
Sur RFC 1861 le extern type
a été introduite. Les types externes sont également des DST, mais les pointeurs vers eux sont no les pointeurs gras. Ou plus exactement, comme le dit le RFC :
Dans Rust, les pointeurs vers les DSTs portent des métadonnées sur l'objet pointé. Pour les chaînes et les tranches, il s'agit de la longueur du tampon, pour les objets trait, il s'agit de la vtable de l'objet. Pour les types externes, les métadonnées sont simplement
()
. Cela signifie qu'un pointeur vers un type externe a la même taille qu'un pointeur vers un type externe.usize
(c'est-à-dire qu'il ne s'agit pas d'un "gros pointeur").
Mais si vous n'interagissez pas avec une interface C, vous n'aurez probablement jamais à vous occuper de ces types externes.
Ci-dessus, nous avons vu les tailles pour les références immuables. Les pointeurs gras fonctionnent de la même manière pour les références mutables, les pointeurs bruts immuables et les pointeurs bruts mutables :
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16
18 votes
Le terme lui-même n'est pas spécifique à Rust, BTW. Pointeur gras fait généralement référence à un pointeur qui stocke des données supplémentaires en plus de la simple adresse de l'objet pointé. Si le pointeur contient des bits de balises et en fonction de ces bits de marquage, le pointeur n'est parfois pas du tout un pointeur, il est appelé un représentation de pointeur étiqueté . (Par exemple, sur de nombreuses VM Smalltalks, les pointeurs qui se terminent par un bit 1 sont en fait des entiers 31/63 bits, puisque les pointeurs sont alignés sur les mots et ne se terminent donc jamais par 1). La JVM HotSpot appelle ses pointeurs gras OOP s (pointeurs orientés objet).
2 votes
Juste une suggestion : lorsque je publie une paire de questions-réponses, j'écris généralement une petite note expliquant qu'il s'agit d'une question à laquelle j'ai répondu moi-même, et pourquoi j'ai décidé de la publier. Jetez un coup d'œil à la note de bas de page de la question ici : stackoverflow.com/q/46147231/5768908
1 votes
@GerardoFurtado J'ai initialement posté un commentaire ici expliquant exactement cela. Mais il a été supprimé maintenant (pas par moi). Mais oui, je suis d'accord, une telle note est souvent utile !
1 votes
@Jörg W Mittag des "pointeurs d'objets ordinaires", en fait