Rust 2018 est sorti ... mais qu'est-ce que c'est?

Cet article a été écrit par Lin Clarke en collaboration avec l'équipe de développement de Rust («nous» dans le texte). Vous pouvez également lire l' article sur le blog officiel de Rust.

La première version de Rust 2018 a été publiée le 6 décembre 2018. Dans cette version, nous nous sommes concentrés sur la productivité afin que les développeurs de Rust commencent à travailler le plus efficacement possible.


La chronologie montre la transition de la version bêta à Rust 2018 et Rust 2015. Elle est entourée d'icônes pour les outils et quatre domaines: WebAssembly, embarqué, réseau et CLI. Le cercle rouge - productivité du développeur - entoure tout sauf Rust 2015

Mais en général, il n'est pas facile d'expliquer ce qu'est Rust 2018.

Certains le présentent comme une nouvelle version du langage ... quelque chose comme ça, mais pas vraiment. Je dis «pas vraiment», car ici, la «nouvelle version» ne signifie pas les nouvelles versions d'autres langues.

Dans la plupart des autres langues, toutes les nouvelles fonctionnalités ajoutent une nouvelle version. La version précédente n'est pas mise à jour.

Le système Rust fonctionne différemment. Cela est dû à la façon dont le langage se développe. Presque toutes les nouvelles fonctionnalités sont 100% compatibles avec Rust. Ils ne nécessitent aucune modification. Cela signifie qu'il n'y a aucune raison de les limiter au code Rust 2018. Les versions plus récentes du compilateur continueront de prendre en charge le «mode Rust 2015» par défaut.

Mais parfois, le développement d'un langage nécessite de l'innovation, par exemple, une nouvelle syntaxe. Et cette nouvelle syntaxe peut casser les bases de code existantes.

Par exemple, la fonction async/await . Au départ, il n'y avait pas de tels concepts dans Rust. Mais il s'est avéré que ces primitives sont vraiment utiles, elles simplifient l'écriture de code asynchrone.

Pour cette fonction, les mots clés async et await doivent être ajoutés. Mais vous devez faire attention à ne pas casser l'ancien code où async ou await pouvaient être utilisés comme noms de variables.

Ainsi, nous ajoutons des mots clés dans Rust 2018. Bien que la fonction n'ait pas encore été publiée, les mots clés sont désormais réservés. Toutes les modifications incompatibles pour les trois prochaines années de développement (par exemple, l'ajout de nouveaux mots clés) sont effectuées simultanément dans Rust 1.31.



Bien qu'il y ait des modifications incompatibles dans Rust 2018, cela ne signifie pas que votre code se cassera. Même avec des variables async et en await , le code sera compilé. Par défaut, le compilateur fonctionne comme auparavant.

Mais si vous souhaitez utiliser l'une des nouvelles fonctions, vous pouvez choisir le nouveau mode de compilation de Rust 2018. La commande cargo fix vous indiquera si vous devez mettre Ă  jour le code pour utiliser les nouvelles fonctions et automatiser le processus de modification. Ensuite, vous pouvez ajouter edition=2018 Ă  votre Cargo.toml si vous acceptez l'utilisation de nouvelles fonctions.

Ce spécificateur de version dans Cargo.toml ne s'applique pas à l'ensemble du projet et ne s'applique pas à vos dépendances. Il est limité à un rack spécifique. Autrement dit, vous pouvez utiliser les caisses Rust 2015 et Rust 2018 en même temps.



Par conséquent, même lorsque vous utilisez Rust 2018, tout ressemble à peu près à Rust 2015. La plupart des modifications sont implémentées simultanément dans Rust 2018 et Rust 2015. Seules quelques fonctions nécessitent des modifications incompatibles.



Rust 2018 n'est pas seulement des changements dans la langue principale. Non seulement eux.

Rust 2018 est, tout d'abord, une impulsion pour améliorer la productivité des développeurs Rust, principalement grâce à des outils qui sont en dehors du langage, ainsi que par le développement d'applications spécifiques et la compréhension de la façon de faire de Rust le langage de programmation le plus efficace pour ces cas.

Ainsi, vous pouvez représenter Rust 2018 en tant que spécificateur dans Cargo.toml, qui est utilisé pour inclure plusieurs fonctions qui nécessitent des modifications incompatibles ...



Ou vous pouvez l'imaginer à un moment où Rust devient l'un des langages les plus efficaces pour de nombreuses applications - lorsque vous avez besoin de performances, d'une utilisation efficace des ressources ou d'une grande fiabilité.



Nous préférons la deuxième version de la définition. Examinons donc toutes les améliorations apportées en dehors de la langue, puis plongons-nous dans la langue elle-même.

Rouille pour des applications spécifiques


Un langage de programmation ne peut pas être efficace en soi, abstraitement. Il est efficace dans une application particulière. Par conséquent, nous avons compris qu'il n'était pas seulement nécessaire d'améliorer Rust en tant que langage ou outil. Il est également nécessaire de simplifier l'utilisation de la rouille dans certaines zones.



Dans certains cas, cela signifiait la création d'un tout nouvel ensemble d'outils pour un écosystème complètement nouveau. Dans d'autres cas, polissage des fonctions existantes et bonne documentation pour faciliter la montée et l'exécution d'un système fonctionnel.

L'équipe de développement de Rust a formé des groupes de travail dans quatre domaines:

  • Webassembly
  • Applications embarquĂ©es
  • Tâches rĂ©seau
  • Outils de ligne de commande

Webassembly


WebAssembly devait créer un tout nouvel ensemble d'outils.

L'année dernière, WebAssembly a permis de compiler des langages tels que Rust pour les exécuter sur Internet. Depuis lors, Rust est rapidement devenu le meilleur langage pour s'intégrer aux applications Web existantes.



Rust est bien adapté au développement Web pour deux raisons:

  1. L'écosystème Cargo Crash fonctionne comme la plupart des développeurs d'applications Web sont habitués. Combinez un tas de petits modules pour former une application plus grande. Cela signifie que Rust est facile à utiliser exactement là où vous en avez besoin.
  2. La rouille a peu de ressources et ne nécessite aucun temps d'exécution. Vous n'avez pas besoin de beaucoup de code. Si vous avez un petit module qui fait beaucoup de travail informatique, implémentez quelques lignes Rust pour l'accélérer.

En utilisant web-sys et js-sys à partir du code Rust, il est facile d'appeler des API Web comme fetch ou appendChild . Et wasm-bindgen facilite la prise en charge des types de données de niveau supérieur que WebAssembly ne prend pas en charge de manière native.

Après avoir écrit le module Rust WebAssembly, il existe des outils pour le connecter facilement au reste de l'application Web. Vous pouvez utiliser wasm-pack pour lancer automatiquement ces outils et exécuter le module dans npm si vous le souhaitez.

Voir le livre Rust et WebAssembly pour plus d'informations .

Et ensuite?


Après la sortie de Rust 2018, les développeurs prévoient de discuter avec la communauté des directions à suivre.

Applications embarquées


Pour le développement embarqué, il était nécessaire d'augmenter la stabilité des fonctionnalités existantes.

Théoriquement, Rust a toujours été un bon langage pour les applications embarquées. Il s'agit d'une boîte à outils moderne, qui faisait cruellement défaut aux développeurs, et de fonctions de langage de haut niveau très pratiques. Tout cela sans charge inutile sur le CPU et la mémoire. Ainsi, Rust est idéal pour être intégré.

Mais en pratique, cela s'est avéré différemment. Le canal stable n'avait pas les fonctions nécessaires. De plus, pour une utilisation sur des appareils embarqués, il était nécessaire de changer la bibliothèque standard. Cela signifie que les gens devaient compiler leur propre version de la caisse de base Rust (la caisse qui est utilisée dans chaque application Rust pour fournir les blocs de construction de base de Rust - fonctions intégrées et primitives).



En conséquence, les développeurs dépendaient de la version expérimentale de Rust. Et en l'absence de tests automatiques, l'assemblage expérimental n'a souvent pas fonctionné sur les microcontrôleurs.

Pour résoudre ce problème, les développeurs ont essayé de transférer toutes les fonctions nécessaires sur un canal stable, d'ajouter des tests au système CI pour les microcontrôleurs. Cela signifie que la modification d'un composant de bureau ne cassera pas la version intégrée.

Avec de tels changements, le développement de systèmes embarqués sur Rust passe du domaine des expériences avancées au domaine de l'efficacité normale.

Pour plus d'informations, consultez le livre Rust for Embedded Systems .

Et ensuite?


Cette année, Rust a obtenu un très bon soutien pour la célèbre famille ARM Cortex-M. Cependant, de nombreuses architectures ne sont pas encore bien prises en charge. Rust doit être étendu pour fournir un support similaire pour d'autres architectures.

Tâches réseau


Pour travailler sur le réseau, il fallait intégrer une abstraction clé dans le langage: async/await . Ainsi, les développeurs peuvent utiliser des idiomes Rust standard même en code asynchrone.

Dans les tâches réseau, vous devez souvent attendre. Par exemple, une réponse à une demande. Si le code est synchrone, alors le travail sera arrêté: le cœur du processeur sur lequel le code est exécuté ne peut rien faire jusqu'à ce qu'une requête arrive. Mais en code asynchrone, une telle fonction peut être mise en veille, tandis que le cœur du CPU fera le reste.

La programmation asynchrone est également possible dans Rust 2015, et cela présente de nombreux avantages. Dans les applications hautes performances, l'application serveur gérera beaucoup plus de connexions à chaque serveur. Les applications embarquées sur de minuscules CPU monothread optimisent l'utilisation d'un seul thread.

Mais ces avantages s'accompagnent d'un inconvénient majeur: pour un tel code, la vérification d'emprunt ne fonctionne pas et vous devez utiliser des idiomes Rust non standard (et légèrement confus). C'est l'avantage de l' async/await . Cela donne au compilateur les informations nécessaires pour tester les emprunts d'appels de fonction asynchrones.

Les mots clés pour async/await implémentés dans la version 1.31, bien qu'ils ne soient pas actuellement pris en charge par l'implémentation. La plupart du travail est terminé et la fonctionnalité devrait être disponible dans la prochaine version.

Et ensuite?


En plus d'un développement efficace de bas niveau, Rust peut fournir un développement plus efficace d'applications réseau à un niveau supérieur.

De nombreux serveurs effectuent des tâches de routine: analyser les URL ou travailler avec HTTP. Si vous les transformez en composants - des abstractions communes qui sont partagées sous forme de caisses - alors il sera facile de les connecter les uns aux autres, formant toutes sortes de configurations de serveur et de framework.

Un framework Tide expérimental a été créé pour développer et tester des composants.

Outils de ligne de commande


Pour les outils de ligne de commande, il était nécessaire de combiner de petites bibliothèques de bas niveau en abstractions de niveau supérieur et de peaufiner certains outils existants.

Pour certains scripts, bash est idéal. Par exemple, pour simplement appeler d'autres outils shell et passer des données entre eux.

Mais la rouille est une excellente option pour de nombreux autres outils. Par exemple, si vous créez un outil complexe comme ripgrep ou un outil CLI en plus des fonctionnalités d'une bibliothèque existante.

Rust ne nécessite pas d'exécution et se compile en un seul binaire statique, ce qui simplifie la distribution du programme. Et vous obtenez des abstractions de haut niveau qui ne sont pas dans d'autres langages, tels que C et C ++.

Quoi d'autre peut améliorer la rouille? Bien sûr, des abstractions d'un niveau encore plus élevé.

Avec des abstractions de niveau supérieur, une CLI prête à l'emploi est rapidement et facilement assemblée.

Un exemple d'une telle abstraction est la bibliothèque de panique humaine . En l'absence d'une telle bibliothèque, en cas de panne, le code CLI renverra probablement tout le backtracking. Mais ce n'est pas très intéressant pour les utilisateurs. Vous pouvez ajouter une gestion des erreurs personnalisée, mais c'est difficile.

Avec la bibliothèque de panique humaine, la sortie ira automatiquement au fichier de vidage d'erreur. L'utilisateur verra un message informatif proposant de signaler un problème et de télécharger le fichier de vidage.



Commencer à développer des outils CLI est également devenu plus facile. Par exemple, la bibliothèque confy automatise sa configuration. Il ne demande que deux choses:

  • Quel est le nom de l'application?
  • Quels paramètres de configuration souhaitez-vous fournir (que vous dĂ©finissez comme une structure qui peut ĂŞtre sĂ©rialisĂ©e et dĂ©sĂ©rialisĂ©e)?

Confy déterminera tout le reste par lui-même.

Et ensuite?


Nous avons résumé de nombreuses tâches pour la CLI. Mais il y a autre chose à abstraire. Nous allons publier plus de ces bibliothèques de haut niveau.

Outils de rouille




Lorsque vous écrivez dans n'importe quelle langue, vous travaillez avec ses outils: en commençant par l'éditeur et en continuant avec d'autres outils à toutes les étapes de développement et de support.

Cela signifie qu'un langage efficace dépend d'outils efficaces.

Voici quelques nouveaux outils (et améliorations des outils existants) dans Rust 2018.

Prise en charge IDE


Bien sûr, les performances dépendent du transfert rapide et fluide du code de l'esprit du développeur vers l'écran de l'ordinateur. C'est là que le support IDE est crucial. Pour ce faire, nous avons besoin d'outils qui peuvent «expliquer» l'IDE à la signification du code Rust: par exemple, suggérer des options significatives pour l'auto-complétion des chaînes.

Dans Rust 2018, la communauté s'est concentrée sur les fonctionnalités requises par l'IDE. Avec l'avènement de Rust Language Server et IntelliJ Rust, de nombreux IDE prennent désormais entièrement en charge Rust.

Compilation plus rapide


Améliorer les performances du compilateur signifie l'accélérer. C'est ce que nous avons fait.

Auparavant, lorsque vous compiliez la caisse Rust, le compilateur recompilait chaque fichier de la caisse. La compilation incrémentielle est maintenant implémentée: elle ne compile que les parties qui ont changé. Avec d'autres optimisations, cela a rendu le compilateur Rust beaucoup plus rapide.

rustfmt


L'efficacité exige également que nous ne discutions jamais des règles de formatage du code ou que nous ne corrigions manuellement les styles des autres.

L'outil rustfmt aide à cela: il reformatera automatiquement le code selon le style par défaut (par lequel la communauté est parvenue à un consensus ). Rustfmt garantit que tout le code Rust correspond au même style, comme le format clang pour C ++ ou Prettier pour JavaScript.

Clippy


Parfois, il est agréable d'avoir un consultant expérimenté à proximité donnant des conseils sur les meilleures pratiques pour écrire du code. C'est ce que fait Clippy: il vérifie le code lors de sa visualisation et suggère des idiomes standard.

rustfix


Mais si vous avez une ancienne base de code avec des idiomes obsolètes, alors vérifier et corriger indépendamment le code peut être fatigant. Vous voulez juste que quelqu'un apporte des corrections à la base de code entière.

Dans ces cas, rustfix automatise le processus. Il applique simultanément les règles d'outils comme Clippy et met à jour l'ancien code conformément aux idiomes de Rust 2018.

Modifications apportées à Rust lui-même


Les changements dans l'écosystème ont considérablement augmenté l'efficacité de la programmation. Mais certains problèmes ne peuvent être résolus que par des changements dans la langue elle-même.



Comme nous l'avons dit dans l'introduction, la plupart des changements de langue sont entièrement compatibles avec le code Rust existant. Tous ces changements font partie de Rust 2018. Mais puisqu'ils ne cassent rien, ils fonctionnent dans n'importe quel code Rust ... même dans l'ancien.

Examinons les fonctionnalités importantes qui sont ajoutées à toutes les versions. Regardez ensuite une courte liste des fonctionnalités de Rust 2018.

Nouvelles fonctionnalités pour toutes les versions


Voici un petit exemple des nouvelles fonctionnalités qui sont (ou seront) dans toutes les versions du langage.

Vérification d'emprunt plus précise


Un gros avantage de Rust est sa vérification d'emprunt. Il garantit que le code est sûr pour la mémoire. Mais c'est aussi une fonctionnalité assez compliquée pour les débutants de Rust.

Une partie de la difficulté réside dans l'apprentissage de nouveaux concepts. Mais il y a une autre partie ... Tester les emprunts rejette parfois du code qui semble fonctionner du point de vue d'un programmeur qui comprend parfaitement le concept de sécurité de la mémoire.


Vous ne pouvez pas emprunter une variable car elle est déjà empruntée

Cela se produit parce que la durée de vie de l'emprunt était censée s'étendre jusqu'à la fin de son champ - par exemple, jusqu'à la fin de la fonction dans laquelle se trouve la variable.

Cela signifie que même si la variable a fini de travailler avec la valeur et n'essaie plus d'accéder, les autres variables se voient toujours refuser l'accès à cette valeur jusqu'à la fin de la fonction.

Pour corriger la situation, nous avons rendu le chèque plus intelligent. Elle voit maintenant quand la variable a fini d'utiliser la valeur. Après cela, il ne bloque pas l'utilisation des données.



Bien que cela ne soit disponible que dans Rust 2018, mais dans un proche avenir, la fonction sera ajoutée à toutes les autres versions. Nous écrirons bientôt plus sur ce sujet.

Macros procédurales dans la rouille stable


Rust avait des macros avant Rust 1.0. Mais dans Rust 2018, de sérieuses améliorations ont été apportées, par exemple, des macros procédurales sont apparues. Ils vous permettent d'ajouter votre propre syntaxe à Rust.

Rust 2018 propose deux types de macros procédurales:

Macros de fonction


Les macros de type fonction vous permettent de créer des objets qui ressemblent à des appels de fonction normaux, mais qui sont en fait exécutés au moment de la compilation. Ils prennent un code et en donnent un autre, que le compilateur insère ensuite dans le binaire.

Ils existaient avant, mais avec des limites. Une macro ne peut exécuter que l'instruction de correspondance. Il n'a pas eu accès à tous les jetons du code entrant.

Mais avec les macros procédurales, vous obtenez la même entrée que l'analyseur: le même flux de jetons. Cela signifie que vous pouvez créer des macros beaucoup plus puissantes comme des fonctions.

Macros de type attribut


Si vous connaissez les décorateurs dans des langages comme JavaScript, les macros d'attributs sont très similaires. Ils vous permettent d'annoter des fragments de code Rust qui doivent être prétraités et transformés en autre chose.

La macro dérivée fait exactement cela. Lorsque vous le placez sur une structure, le compilateur prend cette structure (après l'avoir analysée en tant que liste de jetons) et la traite. En particulier, il ajoute une implémentation de base des fonctions du trait.

Emprunts plus ergonomiques dans les comparaisons


Il y a un simple changement.

Auparavant, si vous vouliez emprunter quelque chose et essayer de faire correspondre, vous deviez ajouter une syntaxe Ă©trange:



Maintenant, au lieu de &Some(ref s) nous Ă©crivons simplement Some(s) .

Nouvelles fonctionnalités de Rust 2018


La plus petite partie de Rust 2018 est constituée de fonctionnalités spécifiques à cette version. Voici un petit ensemble de changements dans Rust 2018.

Mots clefs


Rust 2018 a ajouté quelques mots clés:

  • try
  • async/await

Ces fonctionnalités ne sont pas encore entièrement implémentées, mais des mots clés sont ajoutés dans Rust 1.31. Ainsi, à l'avenir, nous n'aurons pas à introduire de nouveaux mots clés (qui deviendraient un changement incompatible) lorsque nous implémenterons ces fonctions.

Système modulaire


Une grande douleur pour les débutants de Rust est le système modulaire. Et c'est clair pourquoi. Il était difficile de comprendre pourquoi Rust choisit un module particulier. Pour résoudre ce problème, nous avons apporté quelques modifications au mécanisme de chemin.

Par exemple, si vous avez importé un rack, vous pouvez l'utiliser dans le chemin d'accès au niveau supérieur. Mais si vous déplacez un code vers un sous-module, il ne fonctionnera plus.

 // top level module extern crate serde; // this works fine at the top level impl serde::Serialize for MyType { ... } mod foo { // but it does *not* work in a sub-module impl serde::Serialize for OtherType { ... } } 

Un autre exemple est le préfixe ::, qui est utilisé à la fois pour la racine de la caisse et la caisse externe. Il est difficile de comprendre ce qui nous attend.

Nous l'avons rendu plus explicite. Maintenant, si vous voulez vous référer à la caisse racine, utilisez le préfixe crate::. Ceci n'est qu'une des améliorations pour plus de clarté.

Si vous souhaitez que le code actuel utilise les capacités de Rust 2018, vous devrez probablement mettre à jour le code pour prendre en compte les nouveaux chemins. Mais il n'est pas nécessaire de le faire manuellement. Avant d'ajouter le spécificateur de version à Cargo.toml, exécutez-le simplement cargo fixet rustfixapportez les modifications nécessaires.

Information additionnelle


Toutes les informations sur la nouvelle version de la langue sont contenues dans le Guide Rust 2018 .

Source: https://habr.com/ru/post/fr432564/


All Articles