🚧 Attention, peinture fraîche !

Cette page a été traduite par une seule personne et n'a pas été relue et vérifiée par quelqu'un d'autre ! Les informations peuvent par exemple être erronées, être formulées maladroitement, ou contenir d'autres types de fautes.

Vous pouvez contribuer à l'amélioration de cette page sur sa Pull Request.

L'état de l'art de l'asynchrone en Rust

Certaines parties du Rust asynchrone sont pris en charge avec les mêmes garanties de stabilité que le Rust synchrone. Les autres parties sont en cours de perfectionnement et évolueront dans le temps. Voici ce que vous pouvez attendre du Rust asynchrone :

  • D'excellentes performances à l'exécution des charges de travail en concurrence classiques.
  • Une interaction plus régulière avec les fonctionnalités avancées du langage, comme les durées de vie et l'épinglage.
  • Des contraintes de compatibilité, à la fois entre le code synchrone et asynchrone, et entre les différents environnements d'exécution.
  • Une plus grande exigence de maintenance, à cause de l'évolution continue des environnements d'exécution asynchrones et du langage.

En résumé, le Rust asynchrone est plus difficile à utiliser et peut demander plus de maintenance que le Rust synchrone, mais il vous procure en retour les meilleures performances dans le domaine. Tous les éléments du Rust asynchrone dont en constante amélioration, donc les effets de ces contre-parties s'estomperont avec le temps.

La prise en charge des bibliothèques et du langage

Bien que la programmation asynchrone soit fournie par le coeur de Rust, la plupart des applications asynchrones dépendent des fonctionnalités offertes par les crates de la communauté. Par conséquent, vous devez avoir recours à un mélange de fonctionnalités offertes par le langage et les bibliothèques :

  • Les traits, types et fonctions les plus fondamentaux, comme le trait Future, sont fournis par la bibliothèque standard.
  • La syntaxe async/await est prise en charge directement par le compilateur Rust.
  • De nombreux types, macros et fonctions utilitaires sont fournis par la crate futures. Ils peuvent être utilisés dans de nombreuses applications asynchrones en Rust.
  • L'exécution du code asynchrone, les entrées/sorties, et la création de tâches sont prises en charge par les "environnements d'exécution asynchrone", comme Tokio et async-std. La plupart des applications asynchrones, et certaines crates asynchrones, dépendent d'un environnement d'exécution précis. Vous pouvez consulter la section "L'écosystème asynchrone" pour en savoir plus.

Certaines fonctionnalités du langage auquel vous êtes habitué en Rust synchrone peuvent ne pas encore être disponible en Rust asynchrone. Par exemple, Rust ne vous permet pas encore de déclarer des fonctions asynchrones dans des traits. Par conséquent, vous avez besoin de mettre en place des solutions de substitution pour arriver au même résultat, ce qui peut rendre les choses un peu plus verbeuses.

La compilation et le débogage

Dans la plupart des cas, les erreurs du compilateur et d'exécution du Rust asynchrone fonctionnent de la même manière qu'elles l'ont toujours fait en Rust. Voici quelques différences intéressantes :

Les erreurs de compilation

Les erreurs de compilateur en Rust asynchrone suivent les mêmes règles strictes que le Rust synchrone, mais comme le Rust asynchrone dépend souvent de fonctionnalités du langage plus élaborées, comme les durées de vie et l'épinglage, vous pourriez rencontrer plus régulièrement ces types d'erreurs.

Les erreurs à l'exécution

A chaque fois que le compilateur va rencontrer une fonction asynchrone, il va générer une machine à états en arrière-plan. Les traces de la pile en Rust asynchrone contiennent généralement des informations sur ces machines à états, ainsi que les appels de fonctions de l'environnement d'exécution. Par conséquent, l'interprétation des traces de la pile peut être un peu plus ardue qu'elle le serait en Rust synchrone.

Les nouveaux types d'erreurs

Quelques nouveaux types d'erreurs sont possibles avec Rust asynchrone, par exemple si vous appelez une fonction bloquante à partir d'un contexte asynchrone ou si vous n'implémentez pas correctement le trait Future. Ces erreurs peuvent ne pas être signalées par le compilateur et parfois même ne peuvent pas être couvertes par vos tests unitaires. Le but de ce livre est de vous apprendre les principes fondamentaux pour vous aider à éviter ces pièges.

Remarques à propos de la compatibilité

Le code asynchrone et synchrone ne peuvent pas toujours être combinés librement. Par exemple, vous ne pouvez pas appeler directement une fonction asynchrone à partir d'une fonction synchrone. Le code synchrone et asynchrone ont aussi tendance à favoriser des motifs de conception différents, ce qui peut rendre difficile de combiner du code destiné aux différents environnements.

Et même le code asynchrone ne peut pas être combiné librement. Certaines crates dépendent d'un environnement d'exécution asynchrone pour fonctionner. Si c'est le cas, c'est souvent précisé dans la liste des dépendances de la crate.

Ces problèmes de compatibilité peuvent réduire vos options, donc il vaut mieux faire assez tôt vos recherches sur les environnements d'exécution asynchrone et de leurs crates associées. Une fois que vous vous êtes installé dans un environnement d'exécution, vous n'aurez plus à vous soucier de la compatibilité.

Les performances

Les performances du Rust asynchrone dépend de l'implémentation de l'environnement d'exécution asynchrone que vous choisissez. Même si les environnements d'exécution qui propulsent les applications asynchrones en Rust sont relativement récents, ils sont remarquablement performants pour la plupart des charges de travail.

Ceci étant dit, la plupart des écosystèmes asynchrones prévoient un environnement d'exécution multi-processus. Cela rend plus difficile d'apprécier les bienfaits sur les performances théoriques des applications asynchrone sur un seul processus, appelée aussi synchronisation allégée. Un autre domaine d'application sous-côté est celui des tâches sensibles à la latence, qui sont importantes pour les pilotes, les applications avec interface graphique, parmi d'autres. Ces tâches dépendent de l'environnement d'exécution et/ou de la prise en charge du système d'exploitation pour être orchestrées correctement. Vous pouvez donc espérer une meilleure prise en charge à l'avenir des bibliothèques de ces cas d'usages.