mirror of
https://github.com/rust-lang/rustlings.git
synced 2026-08-14 14:26:56 +00:00
async1: Read input from files
This commit is contained in:
parent
1dd31a8fdd
commit
9e76c96ef8
@ -199,7 +199,7 @@ edition = "2024"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1", features = ["rt"] }
|
tokio = { version = "1", features = ["fs", "macros", "rt", "rt-multi-thread"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|||||||
@ -4,39 +4,38 @@
|
|||||||
// together, they can finish the job much faster.
|
// together, they can finish the job much faster.
|
||||||
//
|
//
|
||||||
// Let's simulate this using asynchronous programming. Each person is
|
// Let's simulate this using asynchronous programming. Each person is
|
||||||
// represented as an asynchronous task, which can be executed concurrently (i.e.
|
// represented as an asynchronous task, which can be executed concurrently.
|
||||||
// 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
|
// 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`.
|
// Rust's standard library. Here, we use the mainstream runtime `tokio`.
|
||||||
let rt = tokio::runtime::Builder::new_current_thread()
|
// The macro `tokio::main` wraps the entire main function in a runtime.
|
||||||
.build()
|
#[tokio::main]
|
||||||
.unwrap();
|
async fn main() {
|
||||||
|
// TODO: Fix the compiler errors by making the spawned function async.
|
||||||
|
let mean_score_a = tokio::spawn(calculate_mean_score("input_files/scores_class_a.txt"));
|
||||||
|
let mean_score_b = tokio::spawn(calculate_mean_score("input_files/scores_class_b.txt"));
|
||||||
|
let mean_score_c = tokio::spawn(calculate_mean_score("input_files/scores_class_c.txt"));
|
||||||
|
|
||||||
let scores_class_a = &[83, 77, 92];
|
// TODO: Await the spawned tasks to check their results.
|
||||||
let scores_class_b = &[84, 88, 96];
|
assert_eq!(mean_score_a, 84); // alice
|
||||||
let scores_class_c = &[71, 83, 76];
|
assert_eq!(mean_score_b, 89); // bob
|
||||||
|
assert_eq!(mean_score_c, 76); // catherine
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Fix the compiler errors by making the spawned function async.
|
// TODO: Fix the compiler errors by making the spawned function async.
|
||||||
let alice = rt.spawn(calculate_mean_score(scores_class_a));
|
fn calculate_mean_score(scores_file: &str) -> usize {
|
||||||
let bob = rt.spawn(calculate_mean_score(scores_class_b));
|
// Read the file asynchronously
|
||||||
let catherine = rt.spawn(calculate_mean_score(scores_class_c));
|
let file = tokio::fs::read_to_string(scores_file).await.unwrap();
|
||||||
|
|
||||||
// Block the runtime on a task that awaits all three calculations.
|
// Initialize the sum and the number of scores
|
||||||
let [mean_score_a, mean_score_b, mean_score_c]: [usize; _] = rt.block_on(async {
|
let mut sum = 0;
|
||||||
[
|
let mut n = 0;
|
||||||
// TODO: "await" all three tasks to fix the compiler error.
|
for line in file.lines() {
|
||||||
alice, bob, catherine,
|
// Parse every line as a score
|
||||||
]
|
let score = line.parse::<usize>().unwrap();
|
||||||
});
|
sum += score;
|
||||||
|
n += 1;
|
||||||
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 {
|
sum / n
|
||||||
let score_sum: usize = score_list.iter().sum();
|
|
||||||
score_sum / score_list.len()
|
|
||||||
}
|
}
|
||||||
|
|||||||
3
input_files/scores_class_a.txt
Normal file
3
input_files/scores_class_a.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
83
|
||||||
|
77
|
||||||
|
92
|
||||||
3
input_files/scores_class_b.txt
Normal file
3
input_files/scores_class_b.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
84
|
||||||
|
88
|
||||||
|
96
|
||||||
3
input_files/scores_class_c.txt
Normal file
3
input_files/scores_class_c.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
71
|
||||||
|
83
|
||||||
|
76
|
||||||
@ -1225,3 +1225,12 @@ the functions "tim", "carl" and "nick".
|
|||||||
|
|
||||||
An async task can wait for another one to complete by "awaiting" it. Add
|
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."""
|
".await" after the three "task_name" variables in the "block_on" call."""
|
||||||
|
|
||||||
|
[[input_files]]
|
||||||
|
name = "scores_class_a.txt"
|
||||||
|
|
||||||
|
[[input_files]]
|
||||||
|
name = "scores_class_b.txt"
|
||||||
|
|
||||||
|
[[input_files]]
|
||||||
|
name = "scores_class_c.txt"
|
||||||
|
|||||||
@ -8,18 +8,23 @@ struct ExerciseInfo<'a> {
|
|||||||
dir: &'a str,
|
dir: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct InputFileInfo<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct InfoFile<'a> {
|
struct InfoFile<'a> {
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
exercises: Vec<ExerciseInfo<'a>>,
|
exercises: Vec<ExerciseInfo<'a>>,
|
||||||
|
input_files: Vec<InputFileInfo<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn include_files(_: TokenStream) -> TokenStream {
|
pub fn include_files(_: TokenStream) -> TokenStream {
|
||||||
let info_file = include_str!("../info.toml");
|
let info_file = include_str!("../info.toml");
|
||||||
let exercises = toml::de::from_str::<InfoFile>(info_file)
|
let info = toml::de::from_str::<InfoFile>(info_file).expect("Failed to parse `info.toml`");
|
||||||
.expect("Failed to parse `info.toml`")
|
let exercises = info.exercises;
|
||||||
.exercises;
|
|
||||||
|
|
||||||
let exercise_files = exercises
|
let exercise_files = exercises
|
||||||
.iter()
|
.iter()
|
||||||
@ -46,11 +51,18 @@ pub fn include_files(_: TokenStream) -> TokenStream {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|dir| format!("../exercises/{dir}/README.md"));
|
.map(|dir| format!("../exercises/{dir}/README.md"));
|
||||||
|
|
||||||
|
let input_file_names = info.input_files.iter().map(|f| f.name);
|
||||||
|
let input_file_paths = info
|
||||||
|
.input_files
|
||||||
|
.iter()
|
||||||
|
.map(|f| format!("../input_files/{}", f.name));
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
EmbeddedFiles {
|
EmbeddedFiles {
|
||||||
info_file: #info_file,
|
info_file: #info_file,
|
||||||
exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files), dir_ind: #dir_inds }),*],
|
exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files), dir_ind: #dir_inds }),*],
|
||||||
exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*]
|
exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*],
|
||||||
|
input_files: &[#(InputFile { name: #input_file_names, content: include_str!(#input_file_paths) }),*],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
|
|||||||
@ -4,39 +4,35 @@
|
|||||||
// together, they can finish the job much faster.
|
// together, they can finish the job much faster.
|
||||||
//
|
//
|
||||||
// Let's simulate this using asynchronous programming. Each person is
|
// Let's simulate this using asynchronous programming. Each person is
|
||||||
// represented as an asynchronous task, which can be executed concurrently (i.e.
|
// represented as an asynchronous task, which can be executed concurrently.
|
||||||
// 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
|
// 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`.
|
// Rust's standard library. Here, we use the mainstream runtime `tokio`.
|
||||||
let rt = tokio::runtime::Builder::new_current_thread()
|
// The macro `tokio::main` wraps the entire main function in a runtime.
|
||||||
.build()
|
#[tokio::main]
|
||||||
.unwrap();
|
async fn main() {
|
||||||
|
let mean_score_a = tokio::spawn(calculate_mean_score("input_files/scores_class_a.txt"));
|
||||||
|
let mean_score_b = tokio::spawn(calculate_mean_score("input_files/scores_class_b.txt"));
|
||||||
|
let mean_score_c = tokio::spawn(calculate_mean_score("input_files/scores_class_c.txt"));
|
||||||
|
|
||||||
let scores_class_a = &[83, 77, 92];
|
assert_eq!(mean_score_a.await.unwrap(), 84); // alice
|
||||||
let scores_class_b = &[84, 88, 96];
|
assert_eq!(mean_score_b.await.unwrap(), 89); // bob
|
||||||
let scores_class_c = &[71, 83, 76];
|
assert_eq!(mean_score_c.await.unwrap(), 76); // catherine
|
||||||
|
|
||||||
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 {
|
async fn calculate_mean_score(scores_file: &str) -> usize {
|
||||||
let score_sum: usize = score_list.iter().sum();
|
// Read the file asynchronously
|
||||||
score_sum / score_list.len()
|
let file = tokio::fs::read_to_string(scores_file).await.unwrap();
|
||||||
|
|
||||||
|
// Initialize the sum and the number of scores
|
||||||
|
let mut sum = 0;
|
||||||
|
let mut n = 0;
|
||||||
|
for line in file.lines() {
|
||||||
|
// Parse every line as a score
|
||||||
|
let score = line.parse::<usize>().unwrap();
|
||||||
|
sum += score;
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum / n
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,12 @@ struct ExerciseFiles {
|
|||||||
dir_ind: usize,
|
dir_ind: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Input files that may be read by exercises.
|
||||||
|
pub struct InputFile {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub content: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
fn create_dir_if_not_exists(path: &str) -> Result<()> {
|
fn create_dir_if_not_exists(path: &str) -> Result<()> {
|
||||||
if let Err(e) = create_dir(path)
|
if let Err(e) = create_dir(path)
|
||||||
&& e.kind() != io::ErrorKind::AlreadyExists
|
&& e.kind() != io::ErrorKind::AlreadyExists
|
||||||
@ -58,6 +64,7 @@ pub struct EmbeddedFiles {
|
|||||||
pub info_file: &'static str,
|
pub info_file: &'static str,
|
||||||
exercise_files: &'static [ExerciseFiles],
|
exercise_files: &'static [ExerciseFiles],
|
||||||
pub exercise_dirs: &'static [ExerciseDir],
|
pub exercise_dirs: &'static [ExerciseDir],
|
||||||
|
pub input_files: &'static [InputFile],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmbeddedFiles {
|
impl EmbeddedFiles {
|
||||||
|
|||||||
@ -144,6 +144,15 @@ pub fn init() -> Result<()> {
|
|||||||
.with_context(|| format!("Failed to create the file {solution_path}"))?;
|
.with_context(|| format!("Failed to create the file {solution_path}"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init input files
|
||||||
|
create_dir("input_files").context("Failed to create the directory `input_files`")?;
|
||||||
|
for input_file in EMBEDDED_FILES.input_files {
|
||||||
|
fs::write(
|
||||||
|
format!("input_files/{}", input_file.name),
|
||||||
|
input_file.content,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
let current_cargo_toml = include_str!("../dev-Cargo.toml");
|
let current_cargo_toml = include_str!("../dev-Cargo.toml");
|
||||||
// Skip the first line (comment).
|
// Skip the first line (comment).
|
||||||
let newline_ind = current_cargo_toml
|
let newline_ind = current_cargo_toml
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user