التعامل مع Futures والمهام
افهموا سمة `Future` وكيفية جدولة المهام وإدارتها داخل بيئة تشغيل غير متزامنة.
التعامل مع Futures والمهام درس مجاني في Learn Rust Coding على CoddyKit. هذا هو الدرس 3 من أصل 3. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Learn Rust Coding، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Learn Rust Coding 3 دروس في المجموع.
بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.
What is a Rust Future?
In asynchronous Rust, a Future is a trait that represents an asynchronous computation which may complete at some point. Think of it as a promise for a value that isn't ready yet.
- It's the core building block for async Rust.
async fns in Rust actually return an anonymous type that implements theFuturetrait.- The value isn't computed immediately; it's computed when the
Futureis "polled" by an executor.
How Futures Make Progress
A Future doesn't run on its own. An executor (like the Tokio runtime) repeatedly "polls" it to check if it has made progress or completed.
- When polled, a
Futurereturns eitherPoll::Pending(not done yet) orPoll::Ready(T)(done, here's the result). - If
Pending, the executor knows to poll it again later when something relevant happens (e.g., I/O finishes). - This polling mechanism is what allows many asynchronous operations to run concurrently on a single thread.
Your async Code is a Future
When you write an async fn, Rust transforms it into a state machine that implements the Future trait. The actual computation only starts when the returned Future is polled.
Let's see a simple async function. It doesn't run until awaited by an executor.
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);
}Futures Become Tasks
While a Future is the definition of an async computation, a task is an active instance of that Future being driven to completion by an executor.
- When you tell an executor (like Tokio) to run a
Future, it wraps it in a task. - The executor then manages this task, polling it whenever it's ready to make progress.
- Tasks are the units of work that the async runtime schedules and executes concurrently.
Spawning Tasks for Concurrency
To run a Future concurrently with other code, you "spawn" it onto the Tokio runtime. This creates a new task that the runtime will manage.
The tokio::spawn function takes a Future and returns a JoinHandle, which you can use to await the task's completion and get its result.
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);
}Getting Results from Tasks
The JoinHandle returned by tokio::spawn is itself a Future. You can .await this handle to wait for the spawned task to complete and retrieve its return value.
If the spawned task panics, awaiting its JoinHandle will return an error.
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.");
}Multiple Concurrent Tasks
The power of tasks and tokio::spawn truly shines when you run multiple operations concurrently. The Tokio runtime efficiently switches between tasks as they become ready.
This allows your program to make progress on many things at once without blocking, even on a single 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.");
}Waiting for All with join!
When you need to wait for several Futures to complete at the same time, tokio::join! is a useful macro. It waits for all given futures concurrently and returns their results as a tuple.
It's similar to awaiting each handle individually, but often more concise for fixed numbers of futures.
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.");
}Handling Task Errors
Asynchronous operations can fail, just like synchronous ones. It's common for Futures to return a Result type, indicating success or failure.
When awaiting a JoinHandle, remember that the result is wrapped in another Result because the task itself might panic. You'll often see handle.await? (if in an async fn returning Result) or 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(())
}Understanding Futures & Tasks
You've learned about Futures as computations and Tasks as their execution instances. Which statement about tokio::spawn and JoinHandle is true?
Recap: Futures and Tasks
Great job! You've now grasped the core concepts of Futures and tasks in Rust's asynchronous ecosystem:
- A
Futureis a trait representing an asynchronous computation that will eventually produce a value. async fns compile down to types that implement theFuturetrait.- A task is an instance of a
Futurethat an executor (like Tokio) actively manages and polls. tokio::spawnis used to create a new task, returning aJoinHandle.- You
.awaitaJoinHandleto get the result of a spawned task.
These building blocks are essential for writing efficient, non-blocking Rust applications!
الأسئلة الشائعة
هل درس «التعامل مع Futures والمهام» مجاني؟
نعم — نص درس «التعامل مع Futures والمهام» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Learn Rust Coding، انتقل إلى CoddyKit PRO. تتضمن دورة Learn Rust Coding 3 دروس في المجموع.
ماذا ستتعلم في «التعامل مع Futures والمهام»؟
افهموا سمة `Future` وكيفية جدولة المهام وإدارتها داخل بيئة تشغيل غير متزامنة. تتمرن على Learn Rust Coding مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Learn Rust Coding؟
لا تُشترط خبرة سابقة. Learn Rust Coding على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 3.
كم من الوقت يستغرق درس «التعامل مع Futures والمهام»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Learn Rust Coding هذا؟
نعم. كل درس في Learn Rust Coding يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- مقدمة إلى Async/Await
- بناء تطبيقات غير متزامنة باستخدام Tokio
- التعامل مع Futures والمهام