Hone logo
Hone
Problems

Concurrent Task Spawning with Error Handling in Rust

This challenge focuses on implementing a system for spawning multiple tasks concurrently in Rust, utilizing threads and channels for communication. The goal is to demonstrate proficiency in Rust's concurrency features, including thread management, channel-based communication, and robust error handling. This is a fundamental skill for building responsive and efficient applications.

Problem Description

You are tasked with creating a function spawn_tasks that takes a vector of functions (closures) as input. Each function represents a task that needs to be executed concurrently. The spawn_tasks function should spawn a new thread for each task, passing a channel to each thread. Each task should execute its function and send a result (or an error message if the function panics) through the channel. The main thread should then collect the results from the channels and store them in a vector.

Key Requirements:

  • Concurrency: Tasks must be executed concurrently using threads.
  • Channel Communication: Use channels to communicate results (or errors) from the spawned threads back to the main thread.
  • Error Handling: If a task panics, the spawned thread should send an error message through the channel instead of the result. The main thread should collect and report these errors.
  • Result Collection: The main thread must collect all results (and errors) from the channels and return them in a vector.
  • Thread Safety: Ensure that the code is thread-safe and avoids data races.

Expected Behavior:

The spawn_tasks function should return a Result<Vec<String>, Vec<String>>. The Vec<String> in the Ok variant contains the results from each task, in the same order as the input tasks. The Vec<String> in the Err variant contains error messages from any tasks that panicked, also in the same order as the input tasks.

Edge Cases to Consider:

  • Empty input vector: Should return an empty Ok(Vec::<String>()).
  • Tasks panicking: Handle panics gracefully and report errors.
  • Channel closure: Ensure channels are properly closed after use.
  • Large number of tasks: Consider potential resource limitations.

Examples

Example 1:

Input: [task1: || Ok("Result 1".to_string()), task2: || Ok("Result 2".to_string())]
Output: Ok(["Result 1", "Result 2"])
Explanation: Two tasks are spawned, each returning a successful result. The main thread collects these results in the order they were provided.

Example 2:

Input: [task1: || Ok("Result 1".to_string()), task2: || panic!("Task 2 panicked")]
Output: Err(["Task 2 panicked"])
Explanation: The first task returns a successful result. The second task panics, and the spawned thread sends the panic message through the channel. The main thread collects the error message.

Example 3:

Input: []
Output: Ok([])
Explanation: An empty vector of tasks is provided. The function should return an empty vector of results.

Constraints

  • The input vector can contain up to 100 tasks.
  • Each task's result string should be no longer than 256 characters.
  • Error messages from panics should be captured as strings.
  • The solution should be reasonably efficient; avoid unnecessary allocations or blocking operations.
  • Use std::sync::mpsc for channel communication.

Notes

  • Consider using std::thread::spawn to create new threads.
  • The Result type returned by each task is Result<String, String>. The Ok variant contains the result string, and the Err variant contains an error message.
  • Think about how to handle the lifetime of the closures passed as tasks. They need to be able to access any necessary data.
  • Remember to properly close the channels to prevent deadlocks.
  • The order of results/errors in the returned vector is crucial. It must match the order of the input tasks.
Loading editor...
rust