Interopérabilité et sujets avancés
Quand les programmes atteignent les limites du langage, ils peuvent appeler du code externe ou demander au compilateur des opérations particulières. Ces possibilités sont puissantes et exigent un contrat plus strict.
Dans les suites et fixtures exécutées pour cette référence, aucun programme tutoriel avec extern, intrinsic, defer, unsafe ou macro n’a été confirmé en compilation et exécution. Les extraits ci-dessous montrent la forme décrite ; consultez l’état du support avant de les utiliser.
Déclarer une fonction externe
Un programme peut avoir besoin d’une procédure dont le corps est fourni par une bibliothèque ou une autre convention d’appel. La grammaire prévoit extern, suivi éventuellement du nom d’une bibliothèque, puis d’une signature sans corps :
extern "systeme" proc ecrire_code(code: i64) -> void;
Cette ligne ne crée pas l’implémentation de ecrire_code. Elle promet qu’une fonction compatible sera disponible au moment de l’édition des liens. Les types, la convention d’appel, le nom du symbole et les règles de propriété mémoire doivent correspondre exactement à l’interface externe.
Intrinsics : opérations fournies par le compilateur
Une déclaration intrinsic nomme une opération dont le comportement est fourni par le compilateur ou le runtime :
intrinsic horloge_ticks() -> u64;
À la différence d’une procédure ordinaire, il n’y a pas de corps Vitte à lire ici. L’implémentation doit venir de la plateforme. Les intrinsics servent notamment aux primitives bas niveau, à la mémoire, aux accès atomiques et à certaines opérations arithmétiques. Une déclaration seule ne garantit pas que chaque backend sait l’abaisser : consultez le contrat de la cible et l’état documenté du backend.
Modificateurs et contexte explicite
La grammaire du langage prévoit plusieurs modificateurs avant les déclarations : pub, export, async, unsafe et comptime. Ils précisent une visibilité ou une intention d’exécution. Leur présence dans la grammaire ne signifie pas que leur comportement complet est disponible pour chaque cible.
La syntaxe prévoit aussi des blocs unsafe, l’instruction asm(...); ou asm "...";, l’opérateur await et le mot move. Ce sont des outils de frontières, de concurrence ou de contrôle de représentation. Il faut d’abord comprendre les règles de sécurité, d’ABI, de synchronisation ou de génération de code qu’ils supposent.
Reporter une action avec defer
Une instruction defer reçoit une expression terminée par un point-virgule ou un bloc. Elle sert à associer une action à la sortie d’une portée, par exemple une opération de nettoyage après l’usage d’une ressource.
proc traiter() {
defer fermer_ressource();
utiliser_ressource();
}
Le détail crucial est le moment où l’action différée s’exécute, notamment en cas de retour anticipé ou de sortie imbriquée. Vérifiez cette règle dans l’implémentation et gardez l’action de nettoyage proche de l’acquisition de la ressource. La grammaire accepte cette forme, mais son intégration n’est pas garantie dans tous les backends.
Macros : transformer du code
La grammaire réserve la déclaration macro. Une macro transforme ou construit du code d’une façon plus générale qu’un simple appel de procédure. La puissance d’une macro rend son expansion moins directe à lire, à diagnostiquer et à déboguer ; son interface mérite donc la même attention qu’une API publique.
Avant d’introduire une macro, demandez-vous si une procédure générique, une forme ou une fonction d’aide ne suffirait pas. Les macros sont un sujet avancé : consultez la spécification et les exemples correspondant précisément à la version du compilateur avant d’en dépendre.
Une règle pour les frontières
Une frontière externe peut contourner les vérifications habituelles. Écrivez la signature au plus près de l’implémentation, documentez les types et conventions attendus, testez plusieurs plateformes et gardez la partie non sûre aussi courte que possible. Si l’outil ne prend pas encore en charge l’opération demandée, un programme qui passe l’analyse syntaxique ne suffira pas à la rendre réelle.
unsafe, extern et intrinsic annoncent une responsabilité supplémentaire ; ils ne vérifient pas automatiquement l’ABI, les pointeurs ou la disponibilité d’une fonction sur votre machine.
Où vérifier les détails
La grammaire canonique décrit les formes syntaxiques. Pour savoir ce que le compilateur prend en charge aujourd’hui, commencez par la page État du support : elle compare les documents du dépôt et signale leurs divergences. Les fichiers de modules/core décrivent les contrats des bibliothèques fondamentales. Lisez ces sources ensemble pour distinguer syntaxe, analyse et exécution réelle.
Évaluer une déclaration externe avant de l’écrire
Une déclaration externe est une frontière entre deux contrats. Le fichier Vitte décrit la signature que le code appelant croit pouvoir utiliser. La bibliothèque ou la plateforme fournit réellement le symbole. Avant d’écrire le wrapper, rassemble au minimum le nom du symbole, la convention d’appel, les types et leur représentation, la règle de propriété mémoire, l’alignement et le comportement des erreurs.
Le mot extern ne traduit pas automatiquement une signature étrangère vers une signature Vitte. Une valeur de taille différente, un booléen représenté autrement ou un objet dont la durée de vie n’est pas contrôlée peuvent rendre l’appel incohérent. Une fonction d’adaptation mince est préférable à la propagation de types bas niveau dans tout le programme.
Réduire et documenter le code non sûr
Pour une opération qui demande unsafe, écrivez les obligations avant l’instruction : quelle adresse est valide, quelle longueur est accessible, qui peut écrire, combien de temps l’objet existe et quelle synchronisation est requise ? Ensuite, enfermez cette opération dans la plus petite procédure possible et publiez une interface sûre si l’appelant ordinaire n’a pas à manipuler directement le pointeur.
Un commentaire utile dit pourquoi chaque obligation tient. « C’est unsafe mais le pointeur est valide » ne donne aucune preuve. Une description concrète pourrait identifier l’allocation qui fournit l’adresse, sa taille, son propriétaire et l’événement qui met fin à sa durée de vie. Même lorsque le langage laisse cette responsabilité au programme, la fiche rend l’hypothèse révisable.
Définir le nettoyage indépendamment de la syntaxe
defer promet une action retardée à la sortie d’une portée, mais les cas importants sont les retours anticipés, les blocs imbriqués et les sorties de boucle. Avant de s’appuyer sur cette construction, écrivez la séquence attendue des événements pour chaque chemin. Vérifiez ensuite que le comportement réel de la version du compilateur correspond à cette séquence.
Si le comportement n’est pas confirmé, ne cachez pas une libération de ressource derrière une syntaxe supposée. Gardez l’acquisition et le nettoyage dans une forme que l’outil accepte et que les tests peuvent observer, jusqu’à ce que la règle de defer soit établie.
Pourquoi les macros demandent une discipline différente
Une macro ajoute une transformation avant ou pendant la compilation. Pour la concevoir, il faut préciser ce qu’elle accepte, ce qu’elle produit, comment les erreurs d’expansion sont signalées et où apparaissent les noms générés. Une macro qui raccourcit quelques caractères mais masque le type produit peut rendre les diagnostics plus difficiles que le code répété qu’elle remplace.
La bonne progression est de commencer par une procédure, un type nommé ou une fonction de bibliothèque. N’envisagez une macro que si la structure à générer est réellement répétitive et si l’expansion peut rester prévisible. La syntaxe macro est décrite, mais la compilation et l’exécution d’un exemple de macro ne sont pas confirmées dans l’audit de cette documentation.
Informations à conserver sur une frontière bas niveau
- La construction Vitte concernée et le chemin de compilation qui la prend en charge.
- La responsabilité laissée au programme appelant.
- La garantie fournie par la plateforme ou le runtime.
- Les cas négatifs et les limites effectivement exécutés.
- Le diagnostic attendu lorsque la fonctionnalité n’est pas disponible.
Une interface externe est un contrat d’ABI
extern déclare une signature dont l’implémentation est fournie en dehors du corps Vitte. Pour que l’appel ait un sens, le nom du symbole, la convention d’appel, la largeur des types, leur disposition en mémoire et le traitement des valeurs de retour doivent correspondre. Une procédure externe n’effectue aucune conversion implicite entre deux conventions qui se ressemblent seulement sur le papier.
Les pointeurs et les chaînes sont des cas sensibles : le code externe et le code Vitte doivent se mettre d’accord sur la longueur, la terminaison, l’alignement et la durée de vie du tampon. Les règles de propriété doivent être précises dans les deux sens. Il est souvent préférable de garder la signature étrangère dans un module interne, puis de fournir une interface Vitte plus sûre et plus simple au reste du programme. Le mot-clé extern seul ne prouve toutefois ni la disponibilité du symbole ni la compatibilité de l’édition des liens.
La responsabilité marquée par unsafe
Une opération non sûre est une opération pour laquelle certaines garanties ne sont pas entièrement établies par le langage ou le compilateur. Une déréférence de pointeur suppose une adresse valide, correctement alignée et encore dans sa durée de vie ; un accès à une ressource partagée peut aussi dépendre d’une règle de synchronisation. Le bloc unsafe rend ce point visible, mais la validité doit venir de l’argumentation et du code qui entoure l’opération.
Une bonne interface limite cette responsabilité à une petite zone. Une procédure sûre peut vérifier une longueur, établir les préconditions et appeler une opération dangereuse une seule fois. Les appelants s’appuient ensuite sur le contrat de cette procédure au lieu de répéter la manipulation d’adresses partout. La documentation doit nommer les invariants précis : origine du pointeur, taille accessible, propriétaire, alignement et événement qui termine la validité.
Nettoyage différé et transformations de code
defer associe une action à la sortie d’une portée. Pour une ressource, l’ordre d’acquisition et de libération doit rester correct en cas de retour anticipé, de branchement et de blocs imbriqués. La syntaxe seule ne suffit pas à déterminer si tous les chemins nettoient la ressource comme attendu ; le comportement runtime de la version utilisée doit être établi avant d’en faire une garantie de sécurité.
Une macro transforme ou génère du code. Contrairement à un appel de procédure, l’expansion peut produire plusieurs déclarations ou adapter une syntaxe avant l’analyse finale. Il devient alors important de comprendre les noms générés, les erreurs d’expansion et le code réellement produit. Un helper ordinaire demeure plus simple lorsque la répétition est une opération de calcul. Les macros et intrinsics sont décrits dans les sources, mais aucun programme tutoriel complet avec ces constructions n’a été confirmé au runtime dans l’audit présenté ici.
Intrinsics et dépendance à la cible
Une intrinsic est une opération dont l’implémentation n’est pas un corps ordinaire Vitte. Le compilateur ou le runtime peut la remplacer par une primitive dépendante de l’architecture : accès à une instruction spéciale, mesure du temps ou opération atomique. Cela crée une dépendance à la cible, à son ABI et parfois à ses capacités matérielles.
Une déclaration d’intrinsic ne garantit pas que tous les backends reconnaissent le nom ou produisent une instruction équivalente. Le contrat doit préciser les cibles visées, les effets de bord, l’ordre mémoire et les cas où l’opération n’existe pas. Une abstraction portable peut fournir plusieurs implémentations ou un chemin de repli, si la version du système de modules permet cette organisation.
Assembly inline et maintenance
Une instruction asm rapproche directement le programme du jeu d’instructions et des conventions du compilateur. Les registres utilisés, les valeurs modifiées, les contraintes d’alignement, les effets de mémoire et le respect des règles d’appel ont tous une importance. Si l’une de ces informations est omise, le compilateur peut optimiser en s’appuyant sur une hypothèse incorrecte.
L’assembly inline est difficile à maintenir lorsque les versions du compilateur ou l’architecture changent. Il convient à une frontière très ciblée, entourée d’une interface testable et documentée. Dans l’état d’audit du dépôt, cette syntaxe est décrite mais aucun exemple tutoriel complet n’est confirmé par une commande d’exécution ; elle ne doit donc pas être présentée comme une capacité runtime disponible partout.