Lavorare con Future e task
Comprenda il trait `Future` e il modo in cui le attività vengono pianificate e gestite all’interno di un ambiente runtime asincrono.
Lavorare con Future e task è una lezione Learn Rust Coding gratuita su CoddyKit. Questa è la lezione 3 di 3. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Learn Rust Coding, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Learn Rust Coding include 3 lezioni in totale.
Che cos'è un Future di Rust?
In Rust asincrono, un Future è un trait che rappresenta un'elaborazione asincrona che potrebbe essere completata in un momento successivo. Pensatelo come a una promessa di un valore che non è ancora disponibile.
- È il componente fondamentale di Rust asincrono.
- Le
async fnin Rust restituiscono effettivamente un tipo anonimo che implementa il traitFuture. - Il valore non viene calcolato immediatamente; viene calcolato quando un executor esegue il "poll" del
Future.
Come avanzano i Future
Un Future non viene eseguito autonomamente. Un executor, come il runtime di Tokio, esegue ripetutamente il "poll" per verificare se ha fatto progressi o se è stato completato.
- Quando viene sottoposto a poll, un
FuturerestituiscePoll::Pending(non ancora completato) oppurePoll::Ready(T)(completato, ecco il risultato). - Se restituisce
Pending, l'executor sa di doverlo sottoporre nuovamente a poll in seguito, quando si verifica un evento rilevante, ad esempio il completamento dell'I/O. - Questo meccanismo di polling consente a molte operazioni asincrone di essere eseguite contemporaneamente su un singolo thread.
Il codice async è un Future
Quando scrivete un' async fn, Rust la trasforma in una macchina a stati che implementa il trait Future. L'elaborazione effettiva inizia solo quando viene eseguito il poll del Future restituito.
Vediamo una semplice funzione async. Non viene eseguita finché un executor non ne attende il completamento.
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);
}I Future diventano attività
Mentre un Future è la definizione di un'elaborazione asincrona, una task è un'istanza attiva di quel Future che un executor porta al completamento.
- Quando chiedete a un executor, come Tokio, di eseguire un
Future, questo lo racchiude in un'attività. - L'executor gestisce quindi questa attività, eseguendo il polling ogni volta che è pronta a fare progressi.
- Le attività sono le unità di lavoro che il runtime asincrono pianifica ed esegue contemporaneamente.
Avviare attività per la concorrenza
Per eseguire un Future contemporaneamente ad altro codice, dovete "spawnarlo" nel runtime di Tokio. In questo modo viene creata una nuova attività che il runtime gestirà.
La funzione tokio::spawn riceve un Future e restituisce un JoinHandle, che potete usare per attendere il completamento dell'attività e ottenerne il risultato.
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);
}Ottenere i risultati dalle attività
Il JoinHandle restituito da tokio::spawn è a sua volta un Future. Potete usare .await su questo handle per attendere il completamento dell'attività avviata e recuperare il valore restituito.
Se l'attività avviata va in panic, l'attesa del relativo JoinHandle restituirà un errore.
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.");
}Più attività concorrenti
La potenza delle attività e di tokio::spawn si manifesta pienamente quando eseguite più operazioni contemporaneamente. Il runtime di Tokio passa in modo efficiente da un'attività all'altra man mano che diventano pronte.
Questo consente al programma di portare avanti molte operazioni contemporaneamente senza bloccarsi, anche su un singolo thread.
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.");
}Attendere tutto con join!
Quando dovete attendere contemporaneamente il completamento di diversi Future, tokio::join! è una macro utile. Attende tutte le future indicate in modo concorrente e ne restituisce i risultati come tupla.
È simile ad attendere ogni handle singolarmente, ma spesso è più concisa quando il numero di future è fisso.
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.");
}Gestire gli errori delle attività
Le operazioni asincrone possono non riuscire, proprio come quelle sincrone. È comune che i Future restituiscano un tipo Result, che indica il successo o il fallimento.
Quando attendete un JoinHandle, ricordate che il risultato è racchiuso in un altro Result, perché l'attività stessa potrebbe andare in panic. Vedrete spesso handle.await? (all'interno di un' async fn che restituisce Result) oppure 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(())
}Comprendere Future e attività
Avete imparato che i Future sono elaborazioni e le attività sono le relative istanze in esecuzione. Quale affermazione su tokio::spawn e JoinHandle è vera?
Riepilogo: Future e attività
Ottimo lavoro! Ora avete compreso i concetti fondamentali dei Future e delle attività nell'ecosistema asincrono di Rust:
- Un
Futureè un trait che rappresenta un'elaborazione asincrona destinata a produrre prima o poi un valore. - Le
async fnvengono compilate in tipi che implementano il traitFuture. - Una task è un'istanza di un
Futureche un executor, come Tokio, gestisce attivamente e sottopone a polling. tokio::spawnserve a creare una nuova attività e restituisce unJoinHandle.- Potete usare
.awaitsu unJoinHandleper ottenere il risultato di un'attività avviata.
Questi componenti fondamentali sono indispensabili per scrivere applicazioni Rust efficienti e non bloccanti!
Domande Frequenti
La lezione «Lavorare con Future e task» è gratuita?
Sì — il testo completo di «Lavorare con Future e task» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Learn Rust Coding, passa a CoddyKit PRO. Il corso Learn Rust Coding include 3 lezioni in totale.
Cosa imparerò in «Lavorare con Future e task»?
Comprenda il trait `Future` e il modo in cui le attività vengono pianificate e gestite all’interno di un ambiente runtime asincrono. Eserciti Learn Rust Coding con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Learn Rust Coding?
Non è richiesta alcuna esperienza precedente. Learn Rust Coding su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 3.
Quanto tempo richiede la lezione «Lavorare con Future e task»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Learn Rust Coding?
Sì. Ogni lezione Learn Rust Coding include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Introduzione ad Async/Await
- Creare applicazioni asincrone con Tokio
- Lavorare con Future e task