HeadlinesBriefing favicon HeadlinesBriefing.com

Visualisation des Vtables Rust : Comment fonctionne dyn Trait en mémoire

Hacker News •
×

Je m'aventure dans Rust et c'est à la fois satisfaisant et déroutant. Jusqu'à présent, j'apprends du livre et du livre de Mara Bos, mais j'ai eu envie de le dissecter moi-même. Mon objectif initial avec ces expériences était de comparer l'approche de Rust en matière de polymorphisme avec celle de C++. Cependant, comme je l'ai réalisé, c'est un peu un piège lorsqu'on essaie de comprendre un nouveau langage à travers un autre pour tenter d'établir des parallèles 1:1. Cela peut sembler aider, mais à la fin, on ne peut pas traiter Rust comme C++ avec une syntaxe différente. Si c'était le cas, il n'y aurait rien de révolutionnaire. Dites, je pense qu'il y a de la valeur à creuser et à comprendre pourquoi. Ainsi, si vous êtes comme moi et que vous devez savoir exactement ce qui se passe en mémoire, pour sentir que vous comprenez vraiment les concepts, j'espère que vous trouverez cet article utile :)Par ailleurs, l'image miniature est une photo de champignon rouille, à qui nous devons le nom de Rust. Credit: gailhampshire from Cradley, Malvern, U.K, CC BY 2.0, via Wikimedia Commons. Vous pouvez trouver tout le code et les expériences sur Git Hub.

Introduction : Le cœur de l'affaire

Ce que nous essayons d'atteindre est assez simple. Disons que nous avons une série de formes : cercles, carrés, triangles, et nous voulons appeler draw() sur chacune d'elles.

Approche C++ #1 : Fonctions virtuelles La première façon qui vient à l'esprit en C++ pour faire ceci est à travers des fonctions virtuelles, qui utilisent le polymorphisme à l'exécution. Le pointeur de table virtuelle reside inside the object, le dispatch virtuel se produit automatiquement.

std::vector<Shape*> shapes = { new Circle(), new Square() };

for (auto* s : shapes)s->draw();

L'équivalent Rust est dyn Trait, c'est ce que nous voulons finalement comprendre. Mais d'abord, regardons une autre façon dont nous pourrions résoudre cela en C++.

Approche C++ #2 : CRTP On peut également prendre la voie CRTP (Patron de template récurrent curieux), qui est essentially polymorphisme à la compilation. Si vous êtes intéressé, cette excellente présentation de Klaus Iglberger était mon introduction initiale au sujet, et celle à laquelle je reviens pour référence.

template<typename Derived>struct Shape {void draw() {static_cast<Derived*>(this)->draw();}}; Essentially, il n'y a pas de tables virtuelles et cela est résolu à la compilation, sacrifiant la lisibilité (c'est vraiment un bouche pleine).

Rust offre une équivalente beaucoup plus directe et simple au CRTP, à savoir la monomorphisation. C'est l'approche que nous allons creuser en premier pour commencer à construire notre modèle mental de ce que Rust a à offrir.

Photo by Jiawei Zhao on Unsplash Dispatch statique Le dispatch statique, également connu sous le nom de génériques, obtient un résultat similaire au CRTP : le compilateur génère une copie séparée de la fonction pour chaque type avec lequel il est appelé. Il n'y a aucun coût à l'exécution, mais les types doivent être connus à la compilation.

trait Draw {fn draw(&self) -> &str;}struct Circle;struct Square;impl Draw for Circle {fn draw(&self) -> &str {