La programmation asynchrone dans Rust est une véritable bête de somme pour les applications modernes. En maitriser tous les arcanes est un atout non négligeable si vous êtes vous-même développeur. Dans cet article, on rentre dans le vif du sujet.
Sommaire
- Qu’est-ce que la programmation asynchrone ?
- Pourquoi choisir l’asynchrone en Rust ?
- Créer un runtine asynchrone avec Tokio
Avant de se lancer
Vous souhaitez maitriser tous les mystères de la programmation asynchrone ? Notre formation Rust en inter et intraentreprise vous donnera toutes les clés en main pour la création d’applications modernes et performantes.
L’équipe Ambient IT
Qu’est-ce que la programmation asynchrone ?
Si l’on devait faire un parallèle, la programmation asynchrone, c’est un peu comme jongler avec plusieurs balles en même temps.
En langage Rust cela veut dire utiliser les futures
ainsi que la syntaxe async/await
qui permet de gérer plusieurs tâches en même temps ou presque.
En quoi l’asynchrone diffère-t-il du synchrone ?
En mode synchrone, vous lancez une tâche, vous attendez qu’elle finisse, vous passez à la suivante, etc.
Avec la méthode asynchrone, vous pouvez lancer une tâche, mettre la réponse en attente et en faire une autre dans la foulée. C’est une différence clé pour comprendre en quoi c’est une sacrée avancée en termes de performance et de réactivité.
Pourquoi choisir l’asynchrone en Rust ?
Rust est sans aucun doute le meilleur langage pour l’asynchrone, car il dispose de « zero-cost abstractions ».
Vous êtes donc assuré de ne pas avoir de surcoût inutile d’exécution. C’est un gros plus pour les applications nécessitant de hautes performances.
Qu’est-ce que async/await en Rust ?
async
transforme une fonction en une Future
, un type représentant une valeur qui sera disponible éventuellement. await
est utilisé pour attendre le résultat d’une Future
sans bloquer le thread.
Pour bien comprendre la programmation Asynchrone, il faut comprendre les commandes :
async
transforme une fonction en unefuture
, un type représentant une valeur qui sera disponible dans le… futurawait
est utilisé pour faire patienter le résultat d’unefuture
sans bloquer le thread.
Futures, tâches, executors : quels sont les concepts clés ?
Les Futures
sont au cœur de l’asynchrone en Rust. Une tâche est une Future
qui est exécutée par un executor, qui gère la répartition des tâches sur les Threads disponibles.
Un des concepts phares est de poller un future
. Il s’agit de vérifier si la valeur est prête ou si elle doit être de nouveau mise en attente.
L’executor est lui responsable de prendre une future et l’exécuter jusqu’à ce qu’elle soit bien complète. En d’autres termes, il gère le cycle de vie des futures et assure leur progression.
Comment Rust implémente-t-il l’asynchrone ?
Ici, tout tourne autour du trait Future
et de la pile de runtime asynchrone choisie. Les plus communes sont Tokio et async-std, mais il en existe plein d’autres.
Runtimes et Librairies
Tokio et async-std sont les deux runtimes les plus populaires. Ils offrent des environnements d’exécution pour les Futures
et des bibliothèques de composants asynchrones
Tokio est souvent préféré par les développeurs, car considéré comme plus performant. Il dispose en tout cas d’un environnement plus mature pour les applications réseau.
Créer un runtine asynchrone avec Tokio
Créer un runtime asynchrone avec Tokio est une tâche beaucoup plus simple qu’il n’y parait, alors, laissez-vous guider.
Prérequis
Vous aurez besoin de Rust installé sur votre machine et savoir comment l’utiliser
Étape 1 : mise en place du projet
Vous devez d’abord créer un nouveau projet dans Rust en utilisant cargo.
cargo new my_async_app
cd my_async_app
Étape 2 : ajout des dépendances
Il faut maintenant modifier le fichier Cargo.toml
pour inclure Tokio en tant que dépendance. Je vous conseille simplement d’activer la fonction « full » qui vous donnera accès aux utilitaires les plus communément utilisés.
[dependencies]
tokio = { version = "1", features = ["full"] }
Étape 3 : écrire du code asynchrone
Ouvrez le fichier src/main.rs
et changez son contenu par le code suivant :
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
println!("Starting the async runtime...");
tokio::spawn(async {
println!("This task runs concurrently with the delay.");
});
sleep(Duration::from_secs(3)).await;
println!("After a 3-second delay");
println!("Exiting the program.");
}
Ce code vous permet d’initialiser un runtime Tokio et d’exécuter un bloc asynchrone :
#[tokio::main]
transforme la fonction main en point d’entrée asynchrone de l’application-
tokio::spawn
crée une tache asynchrone qui s’exécute en même temps que d’autres parties -
sleep
est une opération asynchrone qui cède le contrôle sans bloquer le thread en simulant un délai
Étape 4 : lancer l’application
Il ne vous reste plus qu’à utiliser Cargo pour lancer votre application.
cargo run
Cela devrait vous permettre de voir la sortie qui montre vos tâches asynchrones, y compris les délais :
Starting the async runtime...
This task runs concurrently with the delay.
After a 3-second delay
Exiting the program.
Cette méthode avec Tokio est particulièrement utile pour le déploiement d’applications qui demande des grosses performances et une excellente évolutivité.
Dans mon exemple, je vous donne une application asynchrone assez basique (bien que fonctionnelle) mais Tokio vous donne très facilement la possibilité d’étendre cette application en y ajoutant des opérations plus complexes.