diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 4f725b70..59c69c12 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -188,6 +188,8 @@ bin = [ { name = "try_from_into_sol", path = "../solutions/23_conversions/try_from_into.rs" }, { name = "as_ref_mut", path = "../exercises/23_conversions/as_ref_mut.rs" }, { name = "as_ref_mut_sol", path = "../solutions/23_conversions/as_ref_mut.rs" }, + { name = "async1", path = "../exercises/24_async/async1.rs" }, + { name = "async1_sol", path = "../solutions/24_async/async1.rs" }, ] [package] @@ -196,6 +198,9 @@ edition = "2024" # Don't publish the exercises on crates.io! publish = false +[dependencies] +tokio = { version = "1", features = ["rt"] } + [profile.release] panic = "abort" diff --git a/exercises/24_async/README.md b/exercises/24_async/README.md new file mode 100644 index 00000000..4e61bc32 --- /dev/null +++ b/exercises/24_async/README.md @@ -0,0 +1,13 @@ +# Async + +Asynchronous programming is a model where tasks are delegated to a runtime that executes them concurrently. +It is particularly efficient for applications where many independent IO-operations are performed, e.g. web servers. + +Rust provides the necessary primitives to do asynchronous programming in the language. +However, Rust's standard library does not include a runtime. +For these exercises, we will use the mainstream runtime called `tokio`. + +## Further information + +- [Fundamentals of Asynchronous Programming](https://doc.rust-lang.org/book/ch17-00-async-await.html) +- [Tokio documentation](https://docs.rs/tokio/latest/tokio/) diff --git a/exercises/24_async/async1.rs b/exercises/24_async/async1.rs new file mode 100644 index 00000000..8c2067ed --- /dev/null +++ b/exercises/24_async/async1.rs @@ -0,0 +1,42 @@ +// Alice is an elementary school teacher who needs to calculate the mean test +// score for three classes she teaches. Instead of calculating them one after +// the other, she decides to ask her friends Bob and Catherine for help. Working +// together, they can finish the job much faster. +// +// Let's simulate this using asynchronous programming. Each person is +// represented as an asynchronous task, which can be executed concurrently (i.e. +// they can be doing the calculations at the same time). + +fn main() { + // Async tasks need to be executed by a "runtime", which is not provided by + // Rust's standard library. Here, we use the mainstream runtime `tokio`. + let rt = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); + + let scores_class_a = &[83, 77, 92]; + let scores_class_b = &[84, 88, 96]; + let scores_class_c = &[71, 83, 76]; + + // TODO: Fix the compiler errors by making the spawned function async. + let alice = rt.spawn(calculate_mean_score(scores_class_a)); + let bob = rt.spawn(calculate_mean_score(scores_class_b)); + let catherine = rt.spawn(calculate_mean_score(scores_class_c)); + + // Block the runtime on a task that awaits all three calculations. + let [mean_score_a, mean_score_b, mean_score_c]: [usize; _] = rt.block_on(async { + [ + // TODO: "await" all three tasks to fix the compiler error. + alice, bob, catherine, + ] + }); + + assert_eq!(mean_score_a, 84); + assert_eq!(mean_score_b, 89); + assert_eq!(mean_score_c, 76); +} + +fn calculate_mean_score(score_list: &[usize]) -> usize { + let score_sum: usize = score_list.iter().sum(); + score_sum / score_list.len() +} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 353d405c..8266ea99 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1199,3 +1199,17 @@ name = "as_ref_mut" dir = "23_conversions" hint = """ Add `AsRef` or `AsMut` as a trait bound to the functions.""" + +# ASYNC + +[[exercises]] +name = "async1" +dir = "24_async" +test = false +hint = """ +Asynchronous runtimes like tokio can only spawn tasks that are defined as async +functions, not regular ones. Add the "async" keyword before the "fn" keyword of +the functions "tim", "carl" and "nick". + +An async task can wait for another one to complete by "awaiting" it. Add +".await" after the three "task_name" variables in the "block_on" call.""" diff --git a/solutions/24_async/async1.rs b/solutions/24_async/async1.rs new file mode 100644 index 00000000..925be766 --- /dev/null +++ b/solutions/24_async/async1.rs @@ -0,0 +1,42 @@ +// Alice is an elementary school teacher who needs to calculate the mean test +// score for three classes she teaches. Instead of calculating them one after +// the other, she decides to ask her friends Bob and Catherine for help. Working +// together, they can finish the job much faster. +// +// Let's simulate this using asynchronous programming. Each person is +// represented as an asynchronous task, which can be executed concurrently (i.e. +// they can be doing the calculations at the same time). + +fn main() { + // Async tasks need to be executed by a "runtime", which is not provided by + // Rust's standard library. Here, we use the mainstream runtime `tokio`. + let rt = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); + + let scores_class_a = &[83, 77, 92]; + let scores_class_b = &[84, 88, 96]; + let scores_class_c = &[71, 83, 76]; + + let alice = rt.spawn(calculate_mean_score(scores_class_a)); + let bob = rt.spawn(calculate_mean_score(scores_class_b)); + let catherine = rt.spawn(calculate_mean_score(scores_class_c)); + + // Block the runtime on a task that awaits all three calculations. + let [mean_score_a, mean_score_b, mean_score_c]: [usize; _] = rt.block_on(async { + [ + alice.await.unwrap(), + bob.await.unwrap(), + catherine.await.unwrap(), + ] + }); + + assert_eq!(mean_score_a, 84); + assert_eq!(mean_score_b, 89); + assert_eq!(mean_score_c, 76); +} + +async fn calculate_mean_score(score_list: &[usize]) -> usize { + let score_sum: usize = score_list.iter().sum(); + score_sum / score_list.len() +}