0Pricing
Learn Rust Coding · Leçon

Travailler avec les futurs et les tâches

Comprenez le trait `Future` et la manière dont les tâches sont planifiées et gérées dans un environnement d’exécution asynchrone.

Travailler avec les futurs et les tâches est une leçon Learn Rust Coding gratuite sur CoddyKit. Ceci est la leçon 3 sur 3. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Learn Rust Coding, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Learn Rust Coding comprend 3 leçons au total.

Qu'est-ce qu'un futur Rust ?

En Rust asynchrone, un Future est un trait qui représente un calcul asynchrone susceptible de s'achever à un moment donné. Considérez-le comme une promesse pour une valeur qui n'est pas encore prête.

  • C'est le composant fondamental de Rust asynchrone.
  • Les async fn en Rust renvoient en réalité un type anonyme qui implémente le trait Future.
  • La valeur n'est pas calculée immédiatement : elle est calculée lorsque le Future est « interrogé » par un exécuteur.

Comment les futurs progressent

Un Future ne s'exécute pas de lui-même. Un exécuteur (comme le moteur d'exécution Tokio) l'« interroge » régulièrement pour vérifier s'il a progressé ou s'il est terminé.

  • Lorsqu'il est interrogé, un Future renvoie soit Poll::Pending (pas encore terminé), soit Poll::Ready(T) (terminé, voici le résultat).
  • En cas de Pending, l'exécuteur sait qu'il devra l'interroger de nouveau plus tard, lorsqu'un événement pertinent se produira (par exemple, la fin d'une entrée-sortie).
  • Ce mécanisme d'interrogation permet à de nombreuses opérations asynchrones de s'exécuter simultanément sur un seul fil d'exécution.

Votre code asynchrone est un futur

Lorsque vous écrivez une async fn, Rust la transforme en une machine à états qui implémente le trait Future. Le calcul réel ne commence que lorsque le Future renvoyé est interrogé.

Examinons une fonction async simple. Elle ne s'exécute pas tant qu'elle n'est pas attendue par un exécuteur.

async fn say_hello() -> String {
    "Hello from a Future!".to_string()
}

#[tokio::main]
async fn main() {
    let future = say_hello(); // This doesn't run the function yet!
    println!("Future created, but not awaited.");
    // To run it, an executor needs to poll it, often via .await
    // let result = future.await;
    // println!("{}", result);
}

Les futurs deviennent des tâches

Alors qu'un Future définit un calcul asynchrone, une tâche est une instance active de ce Future, menée à son terme par un exécuteur.

  • Lorsque vous demandez à un exécuteur (comme Tokio) d'exécuter un Future, il l'encapsule dans une tâche.
  • L'exécuteur gère ensuite cette tâche et l'interroge chaque fois qu'elle est prête à progresser.
  • Les tâches sont les unités de travail que le moteur d'exécution asynchrone planifie et exécute simultanément.

Créer des tâches pour la concurrence

Pour exécuter un Future simultanément avec un autre code, vous le « créez » dans le moteur d'exécution Tokio. Cela crée une nouvelle tâche que le moteur gérera.

La fonction tokio::spawn prend un Future et renvoie un JoinHandle, que vous pouvez utiliser pour attendre la fin de la tâche et obtenir son résultat.

async fn perform_task(id: u8) -> String {
    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
    format!("Task {} finished!", id)
}

#[tokio::main]
async fn main() {
    println!("Main started.");
    let handle = tokio::spawn(async {
        perform_task(1).await
    });
    println!("Task 1 spawned.");
    // We will await 'handle' in the next scene to get the result.
    // let result = handle.await.unwrap();
    // println!("{}", result);
}

Obtenir les résultats des tâches

Le JoinHandle renvoyé par tokio::spawn est lui-même un Future. Vous pouvez attendre ce descripteur avec .await afin d'attendre la fin de la tâche créée et de récupérer sa valeur de retour.

Si la tâche créée provoque une panique, l'attente de son JoinHandle renverra une erreur.

async fn perform_task(id: u8) -> String {
    tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
    format!("Task {} finished!", id)
}

#[tokio::main]
async fn main() {
    println!("Main started.");
    let handle = tokio::spawn(async {
        perform_task(1).await
    });
    println!("Task 1 spawned.");
    let result = handle.await.unwrap(); // Await the handle to get the result
    println!("{}", result);
    println!("Main finished.");
}

Plusieurs tâches simultanées

La puissance des tâches et de tokio::spawn se révèle pleinement lorsque vous exécutez plusieurs opérations simultanément. Le moteur d'exécution Tokio bascule efficacement d'une tâche à l'autre à mesure qu'elles deviennent prêtes.

Votre programme peut ainsi progresser sur de nombreuses opérations à la fois sans être bloqué, même sur un seul fil d'exécution.

async fn long_task(id: u8) -> String {
    tokio::time::sleep(tokio::time::Duration::from_millis(100 + id as u64 * 50)).await;
    format!("Long task {} done!", id)
}

#[tokio::main]
async fn main() {
    println!("Main started.");
    let handle1 = tokio::spawn(long_task(1));
    let handle2 = tokio::spawn(long_task(2));
    println!("Both tasks spawned.");
    let result1 = handle1.await.unwrap();
    let result2 = handle2.await.unwrap();
    println!("{}\n{}", result1, result2);
    println!("Main finished.");
}

Tout attendre avec join!

Lorsque vous devez attendre que plusieurs Futures s'achèvent en même temps, tokio::join! est une macro utile. Elle attend simultanément tous les futurs fournis et renvoie leurs résultats sous forme de tuple.

C'est comparable au fait d'attendre chaque descripteur individuellement, mais cette solution est souvent plus concise pour un nombre fixe de futurs.

async fn fetch_data(source: &str) -> String {
    tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
    format!("Data from {}", source)
}

#[tokio::main]
async fn main() {
    println!("Starting data fetches...");
    let (data_a, data_b) = tokio::join!(
        fetch_data("Server A"),
        fetch_data("Database B")
    );
    println!("Fetched: {}\nFetched: {}", data_a, data_b);
    println!("All fetches complete.");
}

Gérer les erreurs des tâches

Les opérations asynchrones peuvent échouer, tout comme les opérations synchrones. Il est courant que les Futures renvoient un type Result, indiquant une réussite ou un échec.

Lorsque vous attendez un JoinHandle, n'oubliez pas que le résultat est enveloppé dans un autre Result, car la tâche elle-même peut provoquer une panique. Vous verrez souvent handle.await? (dans une async fn renvoyant un Result) ou handle.await.unwrap() / .expect().

async fn might_fail(should_fail: bool) -> Result<String, &'static str> {
    tokio::time::sleep(tokio::time::Duration::from_millis(30)).await;
    if should_fail {
        Err("Oops, something went wrong!")
    } else {
        Ok("Operation successful!".to_string())
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let handle_ok = tokio::spawn(might_fail(false));
    let handle_err = tokio::spawn(might_fail(true));

    let result_ok = handle_ok.await??; // Await handle, then await inner Result
    println!("{}", result_ok);

    match handle_err.await? { // Await handle, then handle inner Result
        Ok(val) => println!("{}", val),
        Err(e) => eprintln!("Error: {}", e),
    }
    Ok(())
}

Comprendre les futurs et les tâches

Vous avez découvert les futurs en tant que calculs et les tâches en tant qu'instances qui les exécutent. Quelle affirmation concernant tokio::spawn et JoinHandle est vraie ?

Récapitulatif&nbsp;: futurs et tâches

Bravo ! Vous maîtrisez désormais les concepts fondamentaux des Futures et des tâches dans l'écosystème asynchrone de Rust :

  • Un Future est un trait représentant un calcul asynchrone qui produira finalement une valeur.
  • Les async fn sont compilées en types qui implémentent le trait Future.
  • Une tâche est une instance d'un Future qu'un exécuteur (comme Tokio) gère et interroge activement.
  • tokio::spawn sert à créer une nouvelle tâche et renvoie un JoinHandle.
  • Vous attendez un JoinHandle avec .await pour obtenir le résultat d'une tâche créée.

Ces composants sont essentiels pour écrire des applications Rust efficaces et non bloquantes !

Questions Fréquemment Posées

La leçon « Travailler avec les futurs et les tâches » est-elle gratuite ?

Oui — le texte complet de « Travailler avec les futurs et les tâches » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Learn Rust Coding, passe à CoddyKit PRO. Le cours Learn Rust Coding comprend 3 leçons au total.

Qu'est-ce que j'apprendrai dans « Travailler avec les futurs et les tâches » ?

Comprenez le trait `Future` et la manière dont les tâches sont planifiées et gérées dans un environnement d’exécution asynchrone. Tu pratiques Learn Rust Coding avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Learn Rust Coding ?

Aucune expérience préalable n'est requise. Learn Rust Coding sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 3.

Combien de temps prend la leçon « Travailler avec les futurs et les tâches » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Learn Rust Coding ?

Oui. Chaque leçon Learn Rust Coding inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Introduction à Async/Await
  2. Créer des applications asynchrones avec Tokio
  3. Travailler avec les futurs et les tâches
← Retour à Learn Rust Coding