From e5efc68a9101d7d7e38263c8a6ee44dda991fc6a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 17:34:48 +0100 Subject: Done macro --- rustlings-macros/Cargo.toml | 12 ++++++ rustlings-macros/src/lib.rs | 95 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 rustlings-macros/Cargo.toml create mode 100644 rustlings-macros/src/lib.rs (limited to 'rustlings-macros') diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml new file mode 100644 index 0000000..0114c8f --- /dev/null +++ b/rustlings-macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rustlings-macros" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.35" diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs new file mode 100644 index 0000000..dd1a588 --- /dev/null +++ b/rustlings-macros/src/lib.rs @@ -0,0 +1,95 @@ +use proc_macro::TokenStream; +use quote::quote; +use std::{fs::read_dir, panic, path::PathBuf}; + +fn path_to_string(path: PathBuf) -> String { + path.into_os_string() + .into_string() + .unwrap_or_else(|original| { + panic!("The path {} is invalid UTF8", original.to_string_lossy()); + }) +} + +#[proc_macro] +pub fn include_files(_: TokenStream) -> TokenStream { + let mut files = Vec::with_capacity(8); + let mut dirs = Vec::with_capacity(128); + + for entry in read_dir("exercises").expect("Failed to open the exercises directory") { + let entry = entry.expect("Failed to read the exercises directory"); + + if entry.file_type().unwrap().is_file() { + let path = entry.path(); + if path.file_name().unwrap() != "README.md" { + files.push(path_to_string(path)); + } + + continue; + } + + let dir_path = entry.path(); + let dir_files = read_dir(&dir_path).unwrap_or_else(|e| { + panic!("Failed to open the directory {}: {e}", dir_path.display()); + }); + let dir_path = path_to_string(dir_path); + let dir_files = dir_files.filter_map(|entry| { + let entry = entry.unwrap_or_else(|e| { + panic!("Failed to read the directory {dir_path}: {e}"); + }); + let path = entry.path(); + + if !entry.file_type().unwrap().is_file() { + panic!("Found {} but expected only files", path.display()); + } + + if path.file_name().unwrap() == "README.md" { + return None; + } + + if path.extension() != Some("rs".as_ref()) { + panic!( + "Found {} but expected only README.md and .rs files", + path.display(), + ); + } + + Some(path_to_string(path)) + }); + + dirs.push(quote! { + EmbeddedFlatDir { + path: #dir_path, + readme: EmbeddedFile { + path: concat!(#dir_path, "/README.md"), + content: ::std::include_bytes!(concat!("../", #dir_path, "/README.md")), + }, + content: vec![ + #(EmbeddedFile { + path: #dir_files, + content: ::std::include_bytes!(concat!("../", #dir_files)), + }),* + ], + } + }); + } + + quote! { + EmbeddedFiles { + info_toml_content: ::std::include_str!("../info.toml"), + exercises_dir: ExercisesDir { + readme: EmbeddedFile { + path: "exercises/README.md", + content: ::std::include_bytes!("../exercises/README.md"), + }, + files: vec![#( + EmbeddedFile { + path: #files, + content: ::std::include_bytes!(concat!("../", #files)), + } + ),*], + dirs: vec![#(#dirs),*], + }, + } + } + .into() +} -- cgit v1.2.3 From dd025391f2f3a4cb0a45e28163b01538b4b525cb Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 17:52:51 +0100 Subject: Make everything static --- rustlings-macros/src/lib.rs | 6 +++--- src/main.rs | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'rustlings-macros') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index dd1a588..d8cd05c 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -63,7 +63,7 @@ pub fn include_files(_: TokenStream) -> TokenStream { path: concat!(#dir_path, "/README.md"), content: ::std::include_bytes!(concat!("../", #dir_path, "/README.md")), }, - content: vec![ + content: &[ #(EmbeddedFile { path: #dir_files, content: ::std::include_bytes!(concat!("../", #dir_files)), @@ -81,13 +81,13 @@ pub fn include_files(_: TokenStream) -> TokenStream { path: "exercises/README.md", content: ::std::include_bytes!("../exercises/README.md"), }, - files: vec![#( + files: &[#( EmbeddedFile { path: #files, content: ::std::include_bytes!(concat!("../", #files)), } ),*], - dirs: vec![#(#dirs),*], + dirs: &[#(#dirs),*], }, } } diff --git a/src/main.rs b/src/main.rs index fed8c11..7822d12 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,13 +35,13 @@ struct EmbeddedFile { struct EmbeddedFlatDir { path: &'static str, readme: EmbeddedFile, - content: Vec, + content: &'static [EmbeddedFile], } struct ExercisesDir { readme: EmbeddedFile, - files: Vec, - dirs: Vec, + files: &'static [EmbeddedFile], + dirs: &'static [EmbeddedFlatDir], } struct EmbeddedFiles { @@ -49,6 +49,8 @@ struct EmbeddedFiles { exercises_dir: ExercisesDir, } +static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -109,7 +111,6 @@ enum Subcommands { } fn main() -> Result<()> { - let embedded_files = rustlings_macros::include_files!(); let args = Args::parse(); if args.command.is_none() { -- cgit v1.2.3 From 39bdd086a775d87115691b830f65e2a438874fec Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 18:18:20 +0100 Subject: Use concat explicitly from std --- rustlings-macros/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'rustlings-macros') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index d8cd05c..598b5c3 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -60,13 +60,13 @@ pub fn include_files(_: TokenStream) -> TokenStream { EmbeddedFlatDir { path: #dir_path, readme: EmbeddedFile { - path: concat!(#dir_path, "/README.md"), - content: ::std::include_bytes!(concat!("../", #dir_path, "/README.md")), + path: ::std::concat!(#dir_path, "/README.md"), + content: ::std::include_bytes!(::std::concat!("../", #dir_path, "/README.md")), }, content: &[ #(EmbeddedFile { path: #dir_files, - content: ::std::include_bytes!(concat!("../", #dir_files)), + content: ::std::include_bytes!(::std::concat!("../", #dir_files)), }),* ], } @@ -84,7 +84,7 @@ pub fn include_files(_: TokenStream) -> TokenStream { files: &[#( EmbeddedFile { path: #files, - content: ::std::include_bytes!(concat!("../", #files)), + content: ::std::include_bytes!(::std::concat!("../", #files)), } ),*], dirs: &[#(#dirs),*], -- cgit v1.2.3 From 394ca402a8883581dc040546b4ca18b07d76a7f2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 23:57:54 +0200 Subject: Remove the info_toml_content field --- rustlings-macros/src/lib.rs | 1 - src/embedded.rs | 1 - src/exercise.rs | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) (limited to 'rustlings-macros') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index 598b5c3..d8da666 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -75,7 +75,6 @@ pub fn include_files(_: TokenStream) -> TokenStream { quote! { EmbeddedFiles { - info_toml_content: ::std::include_str!("../info.toml"), exercises_dir: ExercisesDir { readme: EmbeddedFile { path: "exercises/README.md", diff --git a/src/embedded.rs b/src/embedded.rs index 56b4b61..1e2d677 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -65,7 +65,6 @@ struct ExercisesDir { } pub struct EmbeddedFiles { - pub info_toml_content: &'static str, exercises_dir: ExercisesDir, } diff --git a/src/exercise.rs b/src/exercise.rs index ae47d5e..c9fb331 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -52,7 +52,7 @@ impl InfoFile { if let Ok(file_content) = fs::read_to_string("info.toml") { toml_edit::de::from_str(&file_content) } else { - toml_edit::de::from_str(EMBEDDED_FILES.info_toml_content) + toml_edit::de::from_str(include_str!("../info.toml")) } .context("Failed to parse `info.toml`") } -- cgit v1.2.3 From c3933904f643238eaafe42e7da967c8262fef22a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:51:50 +0200 Subject: Update deps --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- rustlings-macros/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'rustlings-macros') diff --git a/Cargo.lock b/Cargo.lock index a8ffb8e..554db28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "assert_cmd" @@ -598,9 +598,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] diff --git a/Cargo.toml b/Cargo.toml index 83f01c2..285e7df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ license = "MIT" edition = "2021" [workspace.dependencies] -anyhow = "1.0.81" +anyhow = "1.0.82" serde = { version = "1.0.197", features = ["derive"] } toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 0114c8f..79279f5 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -9,4 +9,4 @@ edition.workspace = true proc-macro = true [dependencies] -quote = "1.0.35" +quote = "1.0.36" -- cgit v1.2.3 From d322bcfcec8bd39a66fb5e6c0390e648e060b67c Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 04:04:45 +0200 Subject: Add description --- rustlings-macros/Cargo.toml | 1 + 1 file changed, 1 insertion(+) (limited to 'rustlings-macros') diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 79279f5..095deaa 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "rustlings-macros" +description = "A macros crate intended to be only used by rustlings" version.workspace = true authors.workspace = true license.workspace = true -- cgit v1.2.3 From b9167e9299bfe7c644cdbc2f6e933873f06b1c3f Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 18:19:15 +0200 Subject: Remove redundant checks --- rustlings-macros/src/lib.rs | 11 ++--------- src/info_file.rs | 8 -------- 2 files changed, 2 insertions(+), 17 deletions(-) (limited to 'rustlings-macros') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index d8da666..d95a93a 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -15,8 +15,8 @@ pub fn include_files(_: TokenStream) -> TokenStream { let mut files = Vec::with_capacity(8); let mut dirs = Vec::with_capacity(128); - for entry in read_dir("exercises").expect("Failed to open the exercises directory") { - let entry = entry.expect("Failed to read the exercises directory"); + for entry in read_dir("exercises").expect("Failed to open the `exercises` directory") { + let entry = entry.expect("Failed to read the `exercises` directory"); if entry.file_type().unwrap().is_file() { let path = entry.path(); @@ -46,13 +46,6 @@ pub fn include_files(_: TokenStream) -> TokenStream { return None; } - if path.extension() != Some("rs".as_ref()) { - panic!( - "Found {} but expected only README.md and .rs files", - path.display(), - ); - } - Some(path_to_string(path)) }); diff --git a/src/info_file.rs b/src/info_file.rs index 18e77b9..879609e 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -64,14 +64,6 @@ impl InfoFile { bail!("{NO_EXERCISES_ERR}"); } - let mut names_set = hashbrown::HashSet::with_capacity(slf.exercises.len()); - for exercise in &slf.exercises { - if !names_set.insert(exercise.name.as_str()) { - bail!("Exercise names must all be unique!") - } - } - drop(names_set); - Ok(slf) } } -- cgit v1.2.3 From 2dac8e509bed07c30a98983cfb6b80f73a1582e9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 23 Apr 2024 19:18:25 +0200 Subject: Refactor embedded files to add solutions --- Cargo.lock | 2 + Cargo.toml | 8 +- info.toml | 3 + rustlings-macros/Cargo.toml | 2 + rustlings-macros/src/lib.rs | 99 ++++++-------------- solutions/00_intro/intro1.rs | 1 + solutions/00_intro/intro2.rs | 1 + solutions/01_variables/variables1.rs | 1 + solutions/01_variables/variables2.rs | 1 + solutions/01_variables/variables3.rs | 1 + solutions/01_variables/variables4.rs | 1 + solutions/01_variables/variables5.rs | 1 + solutions/01_variables/variables6.rs | 1 + solutions/02_functions/functions1.rs | 1 + solutions/02_functions/functions2.rs | 1 + solutions/02_functions/functions3.rs | 1 + solutions/02_functions/functions4.rs | 1 + solutions/02_functions/functions5.rs | 1 + solutions/03_if/if1.rs | 1 + solutions/03_if/if2.rs | 1 + solutions/03_if/if3.rs | 1 + solutions/04_primitive_types/primitive_types1.rs | 1 + solutions/04_primitive_types/primitive_types2.rs | 1 + solutions/04_primitive_types/primitive_types3.rs | 1 + solutions/04_primitive_types/primitive_types4.rs | 1 + solutions/04_primitive_types/primitive_types5.rs | 1 + solutions/04_primitive_types/primitive_types6.rs | 1 + solutions/05_vecs/vecs1.rs | 1 + solutions/05_vecs/vecs2.rs | 1 + solutions/06_move_semantics/move_semantics1.rs | 1 + solutions/06_move_semantics/move_semantics2.rs | 1 + solutions/06_move_semantics/move_semantics3.rs | 1 + solutions/06_move_semantics/move_semantics4.rs | 1 + solutions/06_move_semantics/move_semantics5.rs | 1 + solutions/06_move_semantics/move_semantics6.rs | 1 + solutions/07_structs/structs1.rs | 1 + solutions/07_structs/structs2.rs | 1 + solutions/07_structs/structs3.rs | 1 + solutions/08_enums/enums1.rs | 1 + solutions/08_enums/enums2.rs | 1 + solutions/08_enums/enums3.rs | 1 + solutions/09_strings/strings1.rs | 1 + solutions/09_strings/strings2.rs | 1 + solutions/09_strings/strings3.rs | 1 + solutions/09_strings/strings4.rs | 1 + solutions/10_modules/modules1.rs | 1 + solutions/10_modules/modules2.rs | 1 + solutions/10_modules/modules3.rs | 1 + solutions/11_hashmaps/hashmaps1.rs | 1 + solutions/11_hashmaps/hashmaps2.rs | 1 + solutions/11_hashmaps/hashmaps3.rs | 1 + solutions/12_options/options1.rs | 1 + solutions/12_options/options2.rs | 1 + solutions/12_options/options3.rs | 1 + solutions/13_error_handling/errors1.rs | 1 + solutions/13_error_handling/errors2.rs | 1 + solutions/13_error_handling/errors3.rs | 1 + solutions/13_error_handling/errors4.rs | 1 + solutions/13_error_handling/errors5.rs | 1 + solutions/13_error_handling/errors6.rs | 1 + solutions/14_generics/generics1.rs | 1 + solutions/14_generics/generics2.rs | 1 + solutions/15_traits/traits1.rs | 1 + solutions/15_traits/traits2.rs | 1 + solutions/15_traits/traits3.rs | 1 + solutions/15_traits/traits4.rs | 1 + solutions/15_traits/traits5.rs | 1 + solutions/16_lifetimes/lifetimes1.rs | 1 + solutions/16_lifetimes/lifetimes2.rs | 1 + solutions/16_lifetimes/lifetimes3.rs | 1 + solutions/17_tests/tests1.rs | 1 + solutions/17_tests/tests2.rs | 1 + solutions/17_tests/tests3.rs | 1 + solutions/17_tests/tests4.rs | 1 + solutions/18_iterators/iterators1.rs | 1 + solutions/18_iterators/iterators2.rs | 1 + solutions/18_iterators/iterators3.rs | 1 + solutions/18_iterators/iterators4.rs | 1 + solutions/18_iterators/iterators5.rs | 1 + solutions/19_smart_pointers/arc1.rs | 1 + solutions/19_smart_pointers/box1.rs | 1 + solutions/19_smart_pointers/cow1.rs | 1 + solutions/19_smart_pointers/rc1.rs | 1 + solutions/20_threads/threads1.rs | 1 + solutions/20_threads/threads2.rs | 1 + solutions/20_threads/threads3.rs | 1 + solutions/21_macros/macros1.rs | 1 + solutions/21_macros/macros2.rs | 1 + solutions/21_macros/macros3.rs | 1 + solutions/21_macros/macros4.rs | 1 + solutions/22_clippy/clippy1.rs | 1 + solutions/22_clippy/clippy2.rs | 1 + solutions/22_clippy/clippy3.rs | 1 + solutions/23_conversions/as_ref_mut.rs | 1 + solutions/23_conversions/from_into.rs | 1 + solutions/23_conversions/from_str.rs | 1 + solutions/23_conversions/try_from_into.rs | 1 + solutions/23_conversions/using_as.rs | 1 + solutions/quizzes/quiz1.rs | 1 + solutions/quizzes/quiz2.rs | 1 + solutions/quizzes/quiz3.rs | 1 + src/app_state.rs | 33 ++++--- src/embedded.rs | 110 +++++++++-------------- src/exercise.rs | 1 + src/init.rs | 4 +- 105 files changed, 199 insertions(+), 159 deletions(-) create mode 100644 solutions/00_intro/intro1.rs create mode 100644 solutions/00_intro/intro2.rs create mode 100644 solutions/01_variables/variables1.rs create mode 100644 solutions/01_variables/variables2.rs create mode 100644 solutions/01_variables/variables3.rs create mode 100644 solutions/01_variables/variables4.rs create mode 100644 solutions/01_variables/variables5.rs create mode 100644 solutions/01_variables/variables6.rs create mode 100644 solutions/02_functions/functions1.rs create mode 100644 solutions/02_functions/functions2.rs create mode 100644 solutions/02_functions/functions3.rs create mode 100644 solutions/02_functions/functions4.rs create mode 100644 solutions/02_functions/functions5.rs create mode 100644 solutions/03_if/if1.rs create mode 100644 solutions/03_if/if2.rs create mode 100644 solutions/03_if/if3.rs create mode 100644 solutions/04_primitive_types/primitive_types1.rs create mode 100644 solutions/04_primitive_types/primitive_types2.rs create mode 100644 solutions/04_primitive_types/primitive_types3.rs create mode 100644 solutions/04_primitive_types/primitive_types4.rs create mode 100644 solutions/04_primitive_types/primitive_types5.rs create mode 100644 solutions/04_primitive_types/primitive_types6.rs create mode 100644 solutions/05_vecs/vecs1.rs create mode 100644 solutions/05_vecs/vecs2.rs create mode 100644 solutions/06_move_semantics/move_semantics1.rs create mode 100644 solutions/06_move_semantics/move_semantics2.rs create mode 100644 solutions/06_move_semantics/move_semantics3.rs create mode 100644 solutions/06_move_semantics/move_semantics4.rs create mode 100644 solutions/06_move_semantics/move_semantics5.rs create mode 100644 solutions/06_move_semantics/move_semantics6.rs create mode 100644 solutions/07_structs/structs1.rs create mode 100644 solutions/07_structs/structs2.rs create mode 100644 solutions/07_structs/structs3.rs create mode 100644 solutions/08_enums/enums1.rs create mode 100644 solutions/08_enums/enums2.rs create mode 100644 solutions/08_enums/enums3.rs create mode 100644 solutions/09_strings/strings1.rs create mode 100644 solutions/09_strings/strings2.rs create mode 100644 solutions/09_strings/strings3.rs create mode 100644 solutions/09_strings/strings4.rs create mode 100644 solutions/10_modules/modules1.rs create mode 100644 solutions/10_modules/modules2.rs create mode 100644 solutions/10_modules/modules3.rs create mode 100644 solutions/11_hashmaps/hashmaps1.rs create mode 100644 solutions/11_hashmaps/hashmaps2.rs create mode 100644 solutions/11_hashmaps/hashmaps3.rs create mode 100644 solutions/12_options/options1.rs create mode 100644 solutions/12_options/options2.rs create mode 100644 solutions/12_options/options3.rs create mode 100644 solutions/13_error_handling/errors1.rs create mode 100644 solutions/13_error_handling/errors2.rs create mode 100644 solutions/13_error_handling/errors3.rs create mode 100644 solutions/13_error_handling/errors4.rs create mode 100644 solutions/13_error_handling/errors5.rs create mode 100644 solutions/13_error_handling/errors6.rs create mode 100644 solutions/14_generics/generics1.rs create mode 100644 solutions/14_generics/generics2.rs create mode 100644 solutions/15_traits/traits1.rs create mode 100644 solutions/15_traits/traits2.rs create mode 100644 solutions/15_traits/traits3.rs create mode 100644 solutions/15_traits/traits4.rs create mode 100644 solutions/15_traits/traits5.rs create mode 100644 solutions/16_lifetimes/lifetimes1.rs create mode 100644 solutions/16_lifetimes/lifetimes2.rs create mode 100644 solutions/16_lifetimes/lifetimes3.rs create mode 100644 solutions/17_tests/tests1.rs create mode 100644 solutions/17_tests/tests2.rs create mode 100644 solutions/17_tests/tests3.rs create mode 100644 solutions/17_tests/tests4.rs create mode 100644 solutions/18_iterators/iterators1.rs create mode 100644 solutions/18_iterators/iterators2.rs create mode 100644 solutions/18_iterators/iterators3.rs create mode 100644 solutions/18_iterators/iterators4.rs create mode 100644 solutions/18_iterators/iterators5.rs create mode 100644 solutions/19_smart_pointers/arc1.rs create mode 100644 solutions/19_smart_pointers/box1.rs create mode 100644 solutions/19_smart_pointers/cow1.rs create mode 100644 solutions/19_smart_pointers/rc1.rs create mode 100644 solutions/20_threads/threads1.rs create mode 100644 solutions/20_threads/threads2.rs create mode 100644 solutions/20_threads/threads3.rs create mode 100644 solutions/21_macros/macros1.rs create mode 100644 solutions/21_macros/macros2.rs create mode 100644 solutions/21_macros/macros3.rs create mode 100644 solutions/21_macros/macros4.rs create mode 100644 solutions/22_clippy/clippy1.rs create mode 100644 solutions/22_clippy/clippy2.rs create mode 100644 solutions/22_clippy/clippy3.rs create mode 100644 solutions/23_conversions/as_ref_mut.rs create mode 100644 solutions/23_conversions/from_into.rs create mode 100644 solutions/23_conversions/from_str.rs create mode 100644 solutions/23_conversions/try_from_into.rs create mode 100644 solutions/23_conversions/using_as.rs create mode 100644 solutions/quizzes/quiz1.rs create mode 100644 solutions/quizzes/quiz2.rs create mode 100644 solutions/quizzes/quiz3.rs (limited to 'rustlings-macros') diff --git a/Cargo.lock b/Cargo.lock index 1618eec..93617fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,6 +690,8 @@ name = "rustlings-macros" version = "6.0.0-alpha.0" dependencies = [ "quote", + "serde", + "toml_edit", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6181b1e..1e6a24c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,10 @@ authors = [ license = "MIT" edition = "2021" +[workspace.dependencies] +serde = { version = "1.0.198", features = ["derive"] } +toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } + [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" @@ -41,8 +45,8 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } -serde = { version = "1.0.198", features = ["derive"] } -toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } +serde.workspace = true +toml_edit.workspace = true which = "6.0.1" [dev-dependencies] diff --git a/info.toml b/info.toml index 27071a5..6549e1c 100644 --- a/info.toml +++ b/info.toml @@ -236,6 +236,7 @@ Make sure the type is consistent across all arms.""" [[exercises]] name = "quiz1" +dir = "quizzes" mode = "test" hint = "No hints this time ;)" @@ -637,6 +638,7 @@ Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-v [[exercises]] name = "quiz2" +dir = "quizzes" mode = "test" hint = "No hints this time ;)" @@ -870,6 +872,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe [[exercises]] name = "quiz3" +dir = "quizzes" mode = "test" hint = """ To find the best solution to this challenge you're going to need to think back diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 095deaa..f925f69 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -11,3 +11,5 @@ proc-macro = true [dependencies] quote = "1.0.36" +serde.workspace = true +toml_edit.workspace = true diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index d95a93a..0bf3dbd 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -1,86 +1,39 @@ use proc_macro::TokenStream; use quote::quote; -use std::{fs::read_dir, panic, path::PathBuf}; +use serde::Deserialize; -fn path_to_string(path: PathBuf) -> String { - path.into_os_string() - .into_string() - .unwrap_or_else(|original| { - panic!("The path {} is invalid UTF8", original.to_string_lossy()); - }) +#[derive(Deserialize)] +struct ExerciseInfo { + name: String, + dir: String, +} + +#[derive(Deserialize)] +struct InfoFile { + exercises: Vec, } #[proc_macro] pub fn include_files(_: TokenStream) -> TokenStream { - let mut files = Vec::with_capacity(8); - let mut dirs = Vec::with_capacity(128); - - for entry in read_dir("exercises").expect("Failed to open the `exercises` directory") { - let entry = entry.expect("Failed to read the `exercises` directory"); - - if entry.file_type().unwrap().is_file() { - let path = entry.path(); - if path.file_name().unwrap() != "README.md" { - files.push(path_to_string(path)); - } - - continue; - } - - let dir_path = entry.path(); - let dir_files = read_dir(&dir_path).unwrap_or_else(|e| { - panic!("Failed to open the directory {}: {e}", dir_path.display()); - }); - let dir_path = path_to_string(dir_path); - let dir_files = dir_files.filter_map(|entry| { - let entry = entry.unwrap_or_else(|e| { - panic!("Failed to read the directory {dir_path}: {e}"); - }); - let path = entry.path(); - - if !entry.file_type().unwrap().is_file() { - panic!("Found {} but expected only files", path.display()); - } - - if path.file_name().unwrap() == "README.md" { - return None; - } - - Some(path_to_string(path)) - }); - - dirs.push(quote! { - EmbeddedFlatDir { - path: #dir_path, - readme: EmbeddedFile { - path: ::std::concat!(#dir_path, "/README.md"), - content: ::std::include_bytes!(::std::concat!("../", #dir_path, "/README.md")), - }, - content: &[ - #(EmbeddedFile { - path: #dir_files, - content: ::std::include_bytes!(::std::concat!("../", #dir_files)), - }),* - ], - } - }); - } + let exercises = toml_edit::de::from_str::(include_str!("../../info.toml")) + .expect("Failed to parse `info.toml`") + .exercises; + + let exercise_files = exercises + .iter() + .map(|exercise| format!("../exercises/{}/{}.rs", exercise.dir, exercise.name)); + let solution_files = exercises + .iter() + .map(|exercise| format!("../solutions/{}/{}.rs", exercise.dir, exercise.name)); + let dirs = exercises.iter().map(|exercise| &exercise.dir); + let readmes = exercises + .iter() + .map(|exercise| format!("../exercises/{}/README.md", exercise.dir)); quote! { EmbeddedFiles { - exercises_dir: ExercisesDir { - readme: EmbeddedFile { - path: "exercises/README.md", - content: ::std::include_bytes!("../exercises/README.md"), - }, - files: &[#( - EmbeddedFile { - path: #files, - content: ::std::include_bytes!(::std::concat!("../", #files)), - } - ),*], - dirs: &[#(#dirs),*], - }, + exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files) }),*], + exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*] } } .into() diff --git a/solutions/00_intro/intro1.rs b/solutions/00_intro/intro1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/00_intro/intro1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/00_intro/intro2.rs b/solutions/00_intro/intro2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/00_intro/intro2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables1.rs b/solutions/01_variables/variables1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables2.rs b/solutions/01_variables/variables2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables3.rs b/solutions/01_variables/variables3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables4.rs b/solutions/01_variables/variables4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables5.rs b/solutions/01_variables/variables5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables6.rs b/solutions/01_variables/variables6.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions1.rs b/solutions/02_functions/functions1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions2.rs b/solutions/02_functions/functions2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions3.rs b/solutions/02_functions/functions3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions4.rs b/solutions/02_functions/functions4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions5.rs b/solutions/02_functions/functions5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/03_if/if1.rs b/solutions/03_if/if1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/03_if/if1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/03_if/if2.rs b/solutions/03_if/if2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/03_if/if2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/03_if/if3.rs b/solutions/03_if/if3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/03_if/if3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types1.rs b/solutions/04_primitive_types/primitive_types1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types2.rs b/solutions/04_primitive_types/primitive_types2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types3.rs b/solutions/04_primitive_types/primitive_types3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types4.rs b/solutions/04_primitive_types/primitive_types4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types5.rs b/solutions/04_primitive_types/primitive_types5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types6.rs b/solutions/04_primitive_types/primitive_types6.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/05_vecs/vecs1.rs b/solutions/05_vecs/vecs1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/05_vecs/vecs1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/05_vecs/vecs2.rs b/solutions/05_vecs/vecs2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/05_vecs/vecs2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics1.rs b/solutions/06_move_semantics/move_semantics1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics2.rs b/solutions/06_move_semantics/move_semantics2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics3.rs b/solutions/06_move_semantics/move_semantics3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics4.rs b/solutions/06_move_semantics/move_semantics4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics5.rs b/solutions/06_move_semantics/move_semantics5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics6.rs b/solutions/06_move_semantics/move_semantics6.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/07_structs/structs1.rs b/solutions/07_structs/structs1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/07_structs/structs1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/07_structs/structs2.rs b/solutions/07_structs/structs2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/07_structs/structs2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/07_structs/structs3.rs b/solutions/07_structs/structs3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/07_structs/structs3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/08_enums/enums1.rs b/solutions/08_enums/enums1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/08_enums/enums1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/08_enums/enums2.rs b/solutions/08_enums/enums2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/08_enums/enums2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/08_enums/enums3.rs b/solutions/08_enums/enums3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/08_enums/enums3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings1.rs b/solutions/09_strings/strings1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/09_strings/strings1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings2.rs b/solutions/09_strings/strings2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/09_strings/strings2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings3.rs b/solutions/09_strings/strings3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/09_strings/strings3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings4.rs b/solutions/09_strings/strings4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/09_strings/strings4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/10_modules/modules1.rs b/solutions/10_modules/modules1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/10_modules/modules1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/10_modules/modules2.rs b/solutions/10_modules/modules2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/10_modules/modules2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/10_modules/modules3.rs b/solutions/10_modules/modules3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/10_modules/modules3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/11_hashmaps/hashmaps1.rs b/solutions/11_hashmaps/hashmaps1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/11_hashmaps/hashmaps1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/11_hashmaps/hashmaps2.rs b/solutions/11_hashmaps/hashmaps2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/11_hashmaps/hashmaps2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/12_options/options1.rs b/solutions/12_options/options1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/12_options/options1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/12_options/options2.rs b/solutions/12_options/options2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/12_options/options2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/12_options/options3.rs b/solutions/12_options/options3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/12_options/options3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors1.rs b/solutions/13_error_handling/errors1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors2.rs b/solutions/13_error_handling/errors2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors3.rs b/solutions/13_error_handling/errors3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors4.rs b/solutions/13_error_handling/errors4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors5.rs b/solutions/13_error_handling/errors5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/14_generics/generics1.rs b/solutions/14_generics/generics1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/14_generics/generics1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/14_generics/generics2.rs b/solutions/14_generics/generics2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/14_generics/generics2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits1.rs b/solutions/15_traits/traits1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits2.rs b/solutions/15_traits/traits2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits3.rs b/solutions/15_traits/traits3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits4.rs b/solutions/15_traits/traits4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits5.rs b/solutions/15_traits/traits5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/16_lifetimes/lifetimes1.rs b/solutions/16_lifetimes/lifetimes1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/16_lifetimes/lifetimes1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/16_lifetimes/lifetimes2.rs b/solutions/16_lifetimes/lifetimes2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/16_lifetimes/lifetimes2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/16_lifetimes/lifetimes3.rs b/solutions/16_lifetimes/lifetimes3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/16_lifetimes/lifetimes3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests1.rs b/solutions/17_tests/tests1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/17_tests/tests1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests2.rs b/solutions/17_tests/tests2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/17_tests/tests2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests3.rs b/solutions/17_tests/tests3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/17_tests/tests3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests4.rs b/solutions/17_tests/tests4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/17_tests/tests4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators1.rs b/solutions/18_iterators/iterators1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators2.rs b/solutions/18_iterators/iterators2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators4.rs b/solutions/18_iterators/iterators4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators5.rs b/solutions/18_iterators/iterators5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/arc1.rs b/solutions/19_smart_pointers/arc1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/19_smart_pointers/arc1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/box1.rs b/solutions/19_smart_pointers/box1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/19_smart_pointers/box1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/cow1.rs b/solutions/19_smart_pointers/cow1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/19_smart_pointers/cow1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/rc1.rs b/solutions/19_smart_pointers/rc1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/19_smart_pointers/rc1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/20_threads/threads1.rs b/solutions/20_threads/threads1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/20_threads/threads1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/20_threads/threads2.rs b/solutions/20_threads/threads2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/20_threads/threads2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/20_threads/threads3.rs b/solutions/20_threads/threads3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/20_threads/threads3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros1.rs b/solutions/21_macros/macros1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/21_macros/macros1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros2.rs b/solutions/21_macros/macros2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/21_macros/macros2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros3.rs b/solutions/21_macros/macros3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/21_macros/macros3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros4.rs b/solutions/21_macros/macros4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/21_macros/macros4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/22_clippy/clippy1.rs b/solutions/22_clippy/clippy1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/22_clippy/clippy1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/22_clippy/clippy2.rs b/solutions/22_clippy/clippy2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/22_clippy/clippy2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/22_clippy/clippy3.rs b/solutions/22_clippy/clippy3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/22_clippy/clippy3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/as_ref_mut.rs b/solutions/23_conversions/as_ref_mut.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/as_ref_mut.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/from_into.rs b/solutions/23_conversions/from_into.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/from_into.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/from_str.rs b/solutions/23_conversions/from_str.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/from_str.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/try_from_into.rs b/solutions/23_conversions/try_from_into.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/try_from_into.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/using_as.rs b/solutions/23_conversions/using_as.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/using_as.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/quizzes/quiz1.rs b/solutions/quizzes/quiz1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/quizzes/quiz1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/quizzes/quiz2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/quizzes/quiz3.rs b/solutions/quizzes/quiz3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/quizzes/quiz3.rs @@ -0,0 +1 @@ +// TODO diff --git a/src/app_state.rs b/src/app_state.rs index 09de2a3..0767c2b 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -11,11 +11,7 @@ use std::{ process::{Command, Stdio}, }; -use crate::{ - embedded::{WriteStrategy, EMBEDDED_FILES}, - exercise::Exercise, - info_file::ExerciseInfo, -}; +use crate::{embedded::EMBEDDED_FILES, exercise::Exercise, info_file::ExerciseInfo}; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; @@ -100,10 +96,15 @@ impl AppState { exercise_info.name.shrink_to_fit(); let name = exercise_info.name.leak(); + let dir = exercise_info.dir.map(|mut dir| { + dir.shrink_to_fit(); + &*dir.leak() + }); let hint = exercise_info.hint.trim().to_owned(); Exercise { + dir, name, path, mode: exercise_info.mode, @@ -181,10 +182,16 @@ impl AppState { Ok(()) } - fn reset_path(&self, path: &str) -> Result<()> { + fn reset(&self, ind: usize, dir_name: Option<&str>, path: &str) -> Result<()> { if self.official_exercises { return EMBEDDED_FILES - .write_exercise_to_disk(path, WriteStrategy::Overwrite) + .write_exercise_to_disk( + ind, + dir_name.context( + "Official exercises must be nested in the `exercises` directory", + )?, + path, + ) .with_context(|| format!("Failed to reset the exercise {path}")); } @@ -209,11 +216,11 @@ impl AppState { } pub fn reset_current_exercise(&mut self) -> Result<&'static str> { - let path = self.current_exercise().path; self.set_pending(self.current_exercise_ind)?; - self.reset_path(path)?; + let exercise = self.current_exercise(); + self.reset(self.current_exercise_ind, exercise.dir, exercise.path)?; - Ok(path) + Ok(exercise.path) } pub fn reset_exercise_by_ind(&mut self, exercise_ind: usize) -> Result<&'static str> { @@ -221,11 +228,11 @@ impl AppState { bail!(BAD_INDEX_ERR); } - let path = self.exercises[exercise_ind].path; self.set_pending(exercise_ind)?; - self.reset_path(path)?; + let exercise = &self.exercises[exercise_ind]; + self.reset(exercise_ind, exercise.dir, exercise.path)?; - Ok(path) + Ok(exercise.path) } fn next_pending_exercise_ind(&self) -> Option { diff --git a/src/embedded.rs b/src/embedded.rs index eae3099..ccd8dca 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -1,9 +1,10 @@ use std::{ - fs::{create_dir, File, OpenOptions}, + fs::{create_dir, OpenOptions}, io::{self, Write}, - path::Path, }; +use crate::info_file::ExerciseInfo; + pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); #[derive(Clone, Copy)] @@ -13,107 +14,78 @@ pub enum WriteStrategy { } impl WriteStrategy { - fn open>(self, path: P) -> io::Result { - match self { + fn write(self, path: &str, content: &[u8]) -> io::Result<()> { + let file = match self { Self::IfNotExists => OpenOptions::new().create_new(true).write(true).open(path), Self::Overwrite => OpenOptions::new() .create(true) .write(true) .truncate(true) .open(path), - } + }; + + file?.write_all(content) } } -struct EmbeddedFile { - path: &'static str, - content: &'static [u8], +struct ExerciseFiles { + exercise: &'static [u8], + solution: &'static [u8], } -impl EmbeddedFile { - fn write_to_disk(&self, strategy: WriteStrategy) -> io::Result<()> { - strategy.open(self.path)?.write_all(self.content) - } +struct ExerciseDir { + name: &'static str, + readme: &'static [u8], } -struct EmbeddedFlatDir { - path: &'static str, - readme: EmbeddedFile, - content: &'static [EmbeddedFile], -} - -impl EmbeddedFlatDir { +impl ExerciseDir { fn init_on_disk(&self) -> io::Result<()> { - let path = Path::new(self.path); - - if let Err(e) = create_dir(path) { - if e.kind() != io::ErrorKind::AlreadyExists { - return Err(e); + if let Err(e) = create_dir(format!("exercises/{}", self.name)) { + if e.kind() == io::ErrorKind::AlreadyExists { + return Ok(()); } + + return Err(e); } - self.readme.write_to_disk(WriteStrategy::Overwrite) + WriteStrategy::Overwrite.write(&format!("exercises/{}/README.md", self.name), self.readme) } } -struct ExercisesDir { - readme: EmbeddedFile, - files: &'static [EmbeddedFile], - dirs: &'static [EmbeddedFlatDir], -} - pub struct EmbeddedFiles { - exercises_dir: ExercisesDir, + exercise_files: &'static [ExerciseFiles], + exercise_dirs: &'static [ExerciseDir], } impl EmbeddedFiles { - pub fn init_exercises_dir(&self) -> io::Result<()> { + pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> io::Result<()> { create_dir("exercises")?; - self.exercises_dir - .readme - .write_to_disk(WriteStrategy::IfNotExists)?; - - for file in self.exercises_dir.files { - file.write_to_disk(WriteStrategy::IfNotExists)?; - } + WriteStrategy::IfNotExists.write( + "exercises/README.md", + include_bytes!("../exercises/README.md"), + )?; - for dir in self.exercises_dir.dirs { + for dir in self.exercise_dirs { dir.init_on_disk()?; + } - for file in dir.content { - file.write_to_disk(WriteStrategy::IfNotExists)?; - } + for (exercise_info, exercise_files) in exercise_infos.iter().zip(self.exercise_files) { + WriteStrategy::IfNotExists.write(&exercise_info.path(), exercise_files.exercise)?; } Ok(()) } - pub fn write_exercise_to_disk

(&self, path: P, strategy: WriteStrategy) -> io::Result<()> - where - P: AsRef, - { - let path = path.as_ref(); - - if let Some(file) = self - .exercises_dir - .files - .iter() - .find(|file| Path::new(file.path) == path) - { - return file.write_to_disk(strategy); - } - - for dir in self.exercises_dir.dirs { - if let Some(file) = dir.content.iter().find(|file| Path::new(file.path) == path) { - dir.init_on_disk()?; - return file.write_to_disk(strategy); - } - } + pub fn write_exercise_to_disk(&self, exercise_ind: usize, dir_name: &str, path: &str) -> io::Result<()> { + let Some(dir) = self.exercise_dirs.iter().find(|dir| dir.name == dir_name) else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("`{dir_name}` not found in the embedded directories"), + )); + }; - Err(io::Error::new( - io::ErrorKind::NotFound, - format!("{} not found in the embedded files", path.display()), - )) + dir.init_on_disk()?; + WriteStrategy::Overwrite.write(path, self.exercise_files[exercise_ind].exercise) } } diff --git a/src/exercise.rs b/src/exercise.rs index e85efe4..4493a57 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -31,6 +31,7 @@ impl<'a> Display for TerminalFileLink<'a> { } pub struct Exercise { + pub dir: Option<&'static str>, // Exercise's unique name pub name: &'static str, // Exercise's path diff --git a/src/init.rs b/src/init.rs index f210db7..f1a9509 100644 --- a/src/init.rs +++ b/src/init.rs @@ -24,11 +24,11 @@ pub fn init() -> Result<()> { set_current_dir("rustlings") .context("Failed to change the current directory to `rustlings`")?; + let info_file = InfoFile::parse()?; EMBEDDED_FILES - .init_exercises_dir() + .init_exercises_dir(&info_file.exercises) .context("Failed to initialize the `rustlings/exercises` directory")?; - let info_file = InfoFile::parse()?; let current_cargo_toml = include_str!("../dev/Cargo.toml"); // Skip the first line (comment). let newline_ind = current_cargo_toml -- cgit v1.2.3 From 8ebd2f9df232dbe630510b1994c896871aa58208 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 16:15:14 +0200 Subject: Update Cargo.toml files --- Cargo.toml | 13 ++++++++++--- rustlings-macros/Cargo.toml | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'rustlings-macros') diff --git a/Cargo.toml b/Cargo.toml index 1e6a24c..f4615b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,14 @@ exclude = [ ] [workspace.package] -version = "6.0.0-alpha.0" +version = "6.0.0-beta.0" authors = [ "Liv ", + "Mo Bitar ", + # Alumni "Carol (Nichols || Goulding) ", - "Mo ", ] +repository = "https://github.com/rust-lang/rustlings" license = "MIT" edition = "2021" @@ -27,8 +29,13 @@ description = "Small exercises to get you used to reading and writing Rust code! default-run = "rustlings" version.workspace = true authors.workspace = true +repository.workspace = true license.workspace = true edition.workspace = true +keywords = [ + "exercise", + "learning", +] include = [ "/exercises/", "/info.toml", @@ -44,7 +51,7 @@ crossterm = "0.27.0" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } +rustlings-macros = { path = "rustlings-macros", version = "6.0.0-beta.0" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index f925f69..c9c1d2f 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "rustlings-macros" -description = "A macros crate intended to be only used by rustlings" +description = "A macros crate intended to be used only by Rustlings" version.workspace = true authors.workspace = true +repository.workspace = true license.workspace = true edition.workspace = true -- cgit v1.2.3 From ca41f9e2df5512e9c283dfde9867a5329e9751c9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:02:07 +0200 Subject: Prepare for using cargo-release --- .typos.toml | 7 +++++++ Cargo.lock | 4 ++-- Cargo.toml | 8 ++++++-- release-hook.sh | 8 ++++++++ rustlings-macros/Cargo.toml | 3 +++ 5 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 .typos.toml create mode 100755 release-hook.sh (limited to 'rustlings-macros') diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 0000000..a74498a --- /dev/null +++ b/.typos.toml @@ -0,0 +1,7 @@ +[files] +extend-exclude = [ + "CHANGELOG.md", +] + +[default.extend-words] +"ratatui" = "ratatui" diff --git a/Cargo.lock b/Cargo.lock index 23c4887..788cbbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-beta.0" +version = "6.0.0-alpha.0" dependencies = [ "anyhow", "assert_cmd", @@ -698,7 +698,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.0" +version = "6.0.0-alpha.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 31e7456..5f22665 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.0" +version = "6.0.0-alpha.0" authors = [ "Liv ", "Mo Bitar ", @@ -41,6 +41,7 @@ include = [ "/info.toml", "/LICENSE", "/README.md", + "/solutions/", "/src/", ] @@ -52,7 +53,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "6.0.0-beta.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-alpha.0" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" @@ -66,3 +67,6 @@ panic = "abort" [profile.dev] panic = "abort" + +[package.metadata.release] +pre-release-hook = ["./release-hook.sh"] diff --git a/release-hook.sh b/release-hook.sh new file mode 100755 index 0000000..3a2c537 --- /dev/null +++ b/release-hook.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Error out if any command fails +set -e + +typos +cargo outdated -w --exit-code 1 +cargo test --workspace --all-targets diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index c9c1d2f..f9aba66 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -14,3 +14,6 @@ proc-macro = true quote = "1.0.36" serde.workspace = true toml_edit.workspace = true + +[package.metadata.release] +verify = false -- cgit v1.2.3 From 8d45cdb09df7fbff117b91f500ccf82980732a47 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:54:03 +0200 Subject: Fix missing info.toml in the macros crate --- Cargo.toml | 1 - README.md | 4 +++- rustlings-macros/Cargo.toml | 7 ++++--- rustlings-macros/info.toml | 1 + rustlings-macros/src/lib.rs | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) create mode 120000 rustlings-macros/info.toml (limited to 'rustlings-macros') diff --git a/Cargo.toml b/Cargo.toml index ce3cf04..caa139e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,4 +70,3 @@ panic = "abort" [package.metadata.release] pre-release-hook = ["./release-hook.sh"] -verify = false diff --git a/README.md b/README.md index 2164269..4b50290 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,10 @@ This'll also install _Cargo_, Rust's package/project manager. The following command will download and compile Rustlings: + + ```bash -cargo install rustlings --locked +cargo install rustlings@6.0.0-beta.2 --locked ``` ### Initialization diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index f9aba66..20d6776 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -6,6 +6,10 @@ authors.workspace = true repository.workspace = true license.workspace = true edition.workspace = true +include = [ + "/src/", + "/info.toml", +] [lib] proc-macro = true @@ -14,6 +18,3 @@ proc-macro = true quote = "1.0.36" serde.workspace = true toml_edit.workspace = true - -[package.metadata.release] -verify = false diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml new file mode 120000 index 0000000..3795291 --- /dev/null +++ b/rustlings-macros/info.toml @@ -0,0 +1 @@ +../info.toml \ No newline at end of file diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index 0bf3dbd..fc2bcf1 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -15,7 +15,7 @@ struct InfoFile { #[proc_macro] pub fn include_files(_: TokenStream) -> TokenStream { - let exercises = toml_edit::de::from_str::(include_str!("../../info.toml")) + let exercises = toml_edit::de::from_str::(include_str!("../info.toml")) .expect("Failed to parse `info.toml`") .exercises; -- cgit v1.2.3 From d9df809838191962a82e98ff01aaaa73950ba670 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 12 May 2024 17:40:53 +0200 Subject: Optimize embedded dirs --- rustlings-macros/src/lib.rs | 22 +++++++++-- src/app_state.rs | 42 ++++++++------------- src/embedded.rs | 90 ++++++++++++++++++++++++++++++++------------- 3 files changed, 98 insertions(+), 56 deletions(-) (limited to 'rustlings-macros') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index fc2bcf1..4417a4f 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -25,14 +25,28 @@ pub fn include_files(_: TokenStream) -> TokenStream { let solution_files = exercises .iter() .map(|exercise| format!("../solutions/{}/{}.rs", exercise.dir, exercise.name)); - let dirs = exercises.iter().map(|exercise| &exercise.dir); - let readmes = exercises + + let mut dirs = Vec::with_capacity(32); + let mut dir_inds = vec![0; exercises.len()]; + + for (exercise, dir_ind) in exercises.iter().zip(&mut dir_inds) { + // The directory is often the last one inserted. + if let Some(ind) = dirs.iter().rev().position(|dir| *dir == exercise.dir) { + *dir_ind = dirs.len() - 1 - ind; + continue; + } + + dirs.push(exercise.dir.as_str()); + *dir_ind = dirs.len() - 1; + } + + let readmes = dirs .iter() - .map(|exercise| format!("../exercises/{}/README.md", exercise.dir)); + .map(|dir| format!("../exercises/{dir}/README.md")); quote! { EmbeddedFiles { - exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files) }),*], + 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) }),*] } } diff --git a/src/app_state.rs b/src/app_state.rs index 8cb3e46..492be34 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -193,12 +193,12 @@ impl AppState { Ok(()) } - pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { - if ind >= self.exercises.len() { + pub fn set_current_exercise_ind(&mut self, exercise_ind: usize) -> Result<()> { + if exercise_ind >= self.exercises.len() { bail!(BAD_INDEX_ERR); } - self.current_exercise_ind = ind; + self.current_exercise_ind = exercise_ind; self.write() } @@ -215,8 +215,11 @@ impl AppState { self.write() } - pub fn set_pending(&mut self, ind: usize) -> Result<()> { - let exercise = self.exercises.get_mut(ind).context(BAD_INDEX_ERR)?; + pub fn set_pending(&mut self, exercise_ind: usize) -> Result<()> { + let exercise = self + .exercises + .get_mut(exercise_ind) + .context(BAD_INDEX_ERR)?; if exercise.done { exercise.done = false; @@ -229,16 +232,10 @@ impl AppState { // Official exercises: Dump the original file from the binary. // Third-party exercises: Reset the exercise file with `git stash`. - fn reset(&self, ind: usize, dir_name: Option<&str>, path: &str) -> Result<()> { + fn reset(&self, exercise_ind: usize, path: &str) -> Result<()> { if self.official_exercises { return EMBEDDED_FILES - .write_exercise_to_disk( - ind, - dir_name.context( - "Official exercises must be nested in the `exercises` directory", - )?, - path, - ) + .write_exercise_to_disk(exercise_ind, path) .with_context(|| format!("Failed to reset the exercise {path}")); } @@ -265,7 +262,7 @@ impl AppState { pub fn reset_current_exercise(&mut self) -> Result<&'static str> { self.set_pending(self.current_exercise_ind)?; let exercise = self.current_exercise(); - self.reset(self.current_exercise_ind, exercise.dir, exercise.path)?; + self.reset(self.current_exercise_ind, exercise.path)?; Ok(exercise.path) } @@ -277,7 +274,7 @@ impl AppState { self.set_pending(exercise_ind)?; let exercise = &self.exercises[exercise_ind]; - self.reset(exercise_ind, exercise.dir, exercise.path)?; + self.reset(exercise_ind, exercise.path)?; Ok(exercise.path) } @@ -315,18 +312,9 @@ impl AppState { let current_exercise = self.current_exercise(); if self.official_exercises { - let dir_name = current_exercise - .dir - .context("Official exercises must be nested in the `exercises` directory")?; - let solution_path = format!("solutions/{dir_name}/{}.rs", current_exercise.name); - - EMBEDDED_FILES.write_solution_to_disk( - self.current_exercise_ind, - dir_name, - &solution_path, - )?; - - Ok(Some(solution_path)) + EMBEDDED_FILES + .write_solution_to_disk(self.current_exercise_ind, current_exercise.name) + .map(Some) } else { let solution_path = if let Some(dir) = current_exercise.dir { format!("solutions/{dir}/{}.rs", current_exercise.name) diff --git a/src/embedded.rs b/src/embedded.rs index a84e332..23c8d6e 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Error, Result}; +use anyhow::{Context, Error, Result}; use std::{ fs::{create_dir, create_dir_all, OpenOptions}, io::{self, Write}, @@ -34,6 +34,7 @@ impl WriteStrategy { struct ExerciseFiles { exercise: &'static [u8], solution: &'static [u8], + dir_ind: usize, } struct ExerciseDir { @@ -43,11 +44,10 @@ struct ExerciseDir { impl ExerciseDir { fn init_on_disk(&self) -> Result<()> { - let path_prefix = "exercises/"; - let readme_path_postfix = "/README.md"; - let mut dir_path = - String::with_capacity(path_prefix.len() + self.name.len() + readme_path_postfix.len()); - dir_path.push_str(path_prefix); + // 20 = 10 + 10 + // exercises/ + /README.md + let mut dir_path = String::with_capacity(20 + self.name.len()); + dir_path.push_str("exercises/"); dir_path.push_str(self.name); if let Err(e) = create_dir(&dir_path) { @@ -60,10 +60,9 @@ impl ExerciseDir { ); } - let readme_path = { - dir_path.push_str(readme_path_postfix); - dir_path - }; + let mut readme_path = dir_path; + readme_path.push_str("/README.md"); + WriteStrategy::Overwrite.write(&readme_path, self.readme)?; Ok(()) @@ -95,30 +94,71 @@ impl EmbeddedFiles { Ok(()) } - pub fn write_exercise_to_disk( - &self, - exercise_ind: usize, - dir_name: &str, - path: &str, - ) -> Result<()> { - let Some(dir) = self.exercise_dirs.iter().find(|dir| dir.name == dir_name) else { - bail!("`{dir_name}` not found in the embedded directories"); - }; + pub fn write_exercise_to_disk(&self, exercise_ind: usize, path: &str) -> Result<()> { + let exercise_files = &EMBEDDED_FILES.exercise_files[exercise_ind]; + let dir = &EMBEDDED_FILES.exercise_dirs[exercise_files.dir_ind]; dir.init_on_disk()?; - WriteStrategy::Overwrite.write(path, self.exercise_files[exercise_ind].exercise) + WriteStrategy::Overwrite.write(path, exercise_files.exercise) } + // Write the solution file to disk and return its path. pub fn write_solution_to_disk( &self, exercise_ind: usize, - dir_name: &str, - path: &str, - ) -> Result<()> { - let dir_path = format!("solutions/{dir_name}"); + exercise_name: &str, + ) -> Result { + let exercise_files = &EMBEDDED_FILES.exercise_files[exercise_ind]; + let dir = &EMBEDDED_FILES.exercise_dirs[exercise_files.dir_ind]; + + // 14 = 10 + 1 + 3 + // solutions/ + / + .rs + let mut dir_path = String::with_capacity(14 + dir.name.len() + exercise_name.len()); + dir_path.push_str("solutions/"); + dir_path.push_str(dir.name); create_dir_all(&dir_path) .with_context(|| format!("Failed to create the directory {dir_path}"))?; - WriteStrategy::Overwrite.write(path, self.exercise_files[exercise_ind].solution) + let mut solution_path = dir_path; + solution_path.push('/'); + solution_path.push_str(exercise_name); + solution_path.push_str(".rs"); + + WriteStrategy::Overwrite.write(&solution_path, exercise_files.solution)?; + + Ok(solution_path) + } +} + +#[cfg(test)] +mod tests { + use serde::Deserialize; + + use super::*; + + #[derive(Deserialize)] + struct ExerciseInfo { + dir: String, + } + + #[derive(Deserialize)] + struct InfoFile { + exercises: Vec, + } + + #[test] + fn dirs() { + let exercises = toml_edit::de::from_str::(include_str!("../info.toml")) + .expect("Failed to parse `info.toml`") + .exercises; + + assert_eq!(exercises.len(), EMBEDDED_FILES.exercise_files.len()); + + for (exercise, exercise_files) in exercises.iter().zip(EMBEDDED_FILES.exercise_files) { + assert_eq!( + exercise.dir, + EMBEDDED_FILES.exercise_dirs[exercise_files.dir_ind].name, + ); + } } } -- cgit v1.2.3 From 11fda5d70f568e0f528d91dd573447719abe05f4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 01:25:38 +0200 Subject: Move info.toml to rustlings-macros/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the experience for contributors on Windows becuase Windows can't deal with git symbolic links out of the box… --- CONTRIBUTING.md | 2 +- Cargo.toml | 1 - info.toml | 1286 ------------------------------------------ rustlings-macros/info.toml | 1287 ++++++++++++++++++++++++++++++++++++++++++- rustlings-macros/src/lib.rs | 4 +- src/embedded.rs | 3 +- src/info_file.rs | 4 +- 7 files changed, 1295 insertions(+), 1292 deletions(-) delete mode 100644 info.toml mode change 120000 => 100644 rustlings-macros/info.toml (limited to 'rustlings-macros') diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c6a2d17..bc00a6b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,7 @@ Please be patient 😇 - Name the file `exercises/yourTopic/yourTopicN.rs`. - Make sure to put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`. - Add a (possible) solution at `solutions/yourTopic/yourTopicN.rs` with comments and links explaining it. -- Add the [metadata for your exercise](#exercise-metadata) in the `info.toml` file. +- Add the [metadata for your exercise](#exercise-metadata) in the `rustlings-macros/info.toml` file. - Make sure your exercise runs with `rustlings run yourTopicN`. - [Open a pull request](#pull-requests). diff --git a/Cargo.toml b/Cargo.toml index bc10d02..f2015cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ include = [ "/src/", "/exercises/", "/solutions/", - "/info.toml", # A symlink to be able to include `dev/Cargo.toml` although `dev` is excluded. "/dev-Cargo.toml", "/README.md", diff --git a/info.toml b/info.toml deleted file mode 100644 index 4204f27..0000000 --- a/info.toml +++ /dev/null @@ -1,1286 +0,0 @@ -format_version = 1 - -welcome_message = """Is this your first time? Don't worry, Rustlings is made for beginners! -We are going to teach you a lot of things about Rust, but before we can -get started, here are some notes about how Rustlings operates: - -1. The central concept behind Rustlings is that you solve exercises. These - exercises usually contain some compiler or logic errors which cause the - exercise to fail compilation or testing. It's your job to find all errors - and fix them! -2. Make sure to have your editor open in the `rustlings/` directory. Rustlings - will show you the path of the current exercise under the progress bar. Open - the exercise file in your editor, fix errors and save the file. Rustlings will - automatically detect the file change and rerun the exercise. If all errors are - fixed, Rustlings will ask you to move on to the next exercise. -3. If you're stuck on an exercise, enter `h` (or `hint`) to show a hint. -4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! - (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, - other learners do too so you can help each other out! -""" - -final_message = """We hope you enjoyed learning about the various aspects of Rust! -If you noticed any issues, don't hesitate to report them on Github. -You can also contribute your own exercises to help the greater community! - -Before reporting an issue or contributing, please read our guidelines: -https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md -""" - -# INTRO - -# TODO: Update exercise -[[exercises]] -name = "intro1" -dir = "00_intro" -test = false -# TODO: Fix hint -hint = """Enter `n` (or `next`) followed by ENTER to move on to the next exercise""" - -[[exercises]] -name = "intro2" -dir = "00_intro" -test = false -hint = """ -The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" - -# VARIABLES - -[[exercises]] -name = "variables1" -dir = "01_variables" -test = false -hint = """ -The declaration in the first line in the main function is missing a keyword -that is needed in Rust to create a new variable binding.""" - -[[exercises]] -name = "variables2" -dir = "01_variables" -test = false -hint = """ -The compiler message is saying that Rust cannot infer the type that the -variable binding `x` has with what is given here. - -What happens if you annotate the first line in the main function with a type -annotation? - -What if you give `x` a value? - -What if you do both? - -What type should `x` be, anyway? - -What if `x` is the same type as `10`? What if it's a different type?""" - -[[exercises]] -name = "variables3" -dir = "01_variables" -test = false -hint = """ -Oops! In this exercise, we have a variable binding that we've created on in the -first line in the `main` function, and we're trying to use it in the next line, -but we haven't given it a value. - -We can't print out something that isn't there; try giving `x` a value! - -This is an error that can cause bugs that's very easy to make in any -programming language -- thankfully the Rust compiler has caught this for us!""" - -[[exercises]] -name = "variables4" -dir = "01_variables" -test = false -hint = """ -In Rust, variable bindings are immutable by default. But here we're trying -to reassign a different value to `x`! There's a keyword we can use to make -a variable binding mutable instead.""" - -[[exercises]] -name = "variables5" -dir = "01_variables" -test = false -hint = """ -In `variables4` we already learned how to make an immutable variable mutable -using a special keyword. Unfortunately this doesn't help us much in this -exercise because we want to assign a different typed value to an existing -variable. Sometimes you may also like to reuse existing variable names because -you are just converting values to different types like in this exercise. - -Fortunately Rust has a powerful solution to this problem: 'Shadowing'! -You can read more about 'Shadowing' in the book's section 'Variables and -Mutability': -https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing - -Try to solve this exercise afterwards using this technique.""" - -[[exercises]] -name = "variables6" -dir = "01_variables" -test = false -hint = """ -We know about variables and mutability, but there is another important type of -variable available: constants. - -Constants are always immutable and they are declared with keyword `const` rather -than keyword `let`. - -Constants types must also always be annotated. - -Read more about constants and the differences between variables and constants -under 'Constants' in the book's section 'Variables and Mutability': -https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants -""" - -# FUNCTIONS - -[[exercises]] -name = "functions1" -dir = "02_functions" -test = false -hint = """ -This main function is calling a function that it expects to exist, but the -function doesn't exist. It expects this function to have the name `call_me`. -It expects this function to not take any arguments and not return a value. -Sounds a lot like `main`, doesn't it?""" - -[[exercises]] -name = "functions2" -dir = "02_functions" -test = false -hint = """ -Rust requires that all parts of a function's signature have type annotations, -but `call_me` is missing the type annotation of `num`.""" - -[[exercises]] -name = "functions3" -dir = "02_functions" -test = false -hint = """ -This time, the function *declaration* is okay, but there's something wrong -with the place where we're calling the function.""" - -[[exercises]] -name = "functions4" -dir = "02_functions" -test = false -hint = """ -The error message points to the function `sale_price` and says it expects a type -after the `->`. This is where the function's return type should be -- take a -look at the `is_even` function for an example!""" - -[[exercises]] -name = "functions5" -dir = "02_functions" -test = false -hint = """ -This is a really common error that can be fixed by removing one character. -It happens because Rust distinguishes between expressions and statements: -expressions return a value based on their operand(s), and statements simply -return a `()` type which behaves just like `void` in C/C++ language. - -We want to return a value of `i32` type from the `square` function, but it is -returning a `()` type... - -They are not the same. There are two solutions: -1. Add a `return` ahead of `num * num;` -2. remove `;`, make it to be `num * num`""" - -# IF - -[[exercises]] -name = "if1" -dir = "03_if" -hint = """ -It's possible to do this in one line if you would like! - -Some similar examples from other languages: -- In C(++) this would be: `a > b ? a : b` -- In Python this would be: `a if a > b else b` - -Remember in Rust that: -- the `if` condition does not need to be surrounded by parentheses -- `if`/`else` conditionals are expressions -- Each condition is followed by a `{}` block.""" - -[[exercises]] -name = "if2" -dir = "03_if" -hint = """ -For that first compiler error, it's important in Rust that each conditional -block returns the same type! To get the tests passing, you will need a couple -conditions checking different input values.""" - -[[exercises]] -name = "if3" -dir = "03_if" -hint = """ -In Rust, every arm of an `if` expression has to return the same type of value. -Make sure the type is consistent across all arms.""" - -# QUIZ 1 - -[[exercises]] -name = "quiz1" -dir = "quizzes" -hint = "No hints this time ;)" - -# PRIMITIVE TYPES - -[[exercises]] -name = "primitive_types1" -dir = "04_primitive_types" -test = false -hint = "No hints this time ;)" - -[[exercises]] -name = "primitive_types2" -dir = "04_primitive_types" -test = false -hint = "No hints this time ;)" - -[[exercises]] -name = "primitive_types3" -dir = "04_primitive_types" -test = false -hint = """ -There's a shorthand to initialize Arrays with a certain size that does not -require you to type in 100 items (but you certainly can if you want!). - -For example, you can do: -``` -let array = ["Are we there yet?"; 10]; -``` - -Bonus: what are some other things you could have that would return `true` -for `a.len() >= 100`?""" - -[[exercises]] -name = "primitive_types4" -dir = "04_primitive_types" -hint = """ -Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section -of the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the -starting and ending (plus one) indices of the items in the `Array` that you -want to end up in the slice. - -If you're curious why the first argument of `assert_eq!` does not have an -ampersand for a reference since the second argument is a reference, take a look -at the coercion chapter of the nomicon: -https://doc.rust-lang.org/nomicon/coercions.html""" - -[[exercises]] -name = "primitive_types5" -dir = "04_primitive_types" -test = false -hint = """ -Take a look at the 'Data Types -> The Tuple Type' section of the book: -https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type -Particularly the part about destructuring (second to last example in the -section). - -You'll need to make a pattern to bind `name` and `age` to the appropriate parts -of the tuple. You can do it!!""" - -[[exercises]] -name = "primitive_types6" -dir = "04_primitive_types" -hint = """ -While you could use a destructuring `let` for the tuple here, try -indexing into it instead, as explained in the last example of the -'Data Types -> The Tuple Type' section of the book: -https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type -Now you have another tool in your toolbox!""" - -# VECS - -[[exercises]] -name = "vecs1" -dir = "05_vecs" -hint = """ -In Rust, there are two ways to define a Vector. -1. One way is to use the `Vec::new()` function to create a new vector - and fill it with the `push()` method. -2. The second way, which is simpler is to use the `vec![]` macro and - define your elements inside the square brackets. - -Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html -of the Rust book to learn more. -""" - -[[exercises]] -name = "vecs2" -dir = "05_vecs" -hint = """ -In the first function we are looping over the Vector and getting a reference to -one `element` at a time. - -To modify the value of that `element` we need to use the `*` dereference -operator. You can learn more in this chapter of the Rust book: -https://doc.rust-lang.org/stable/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector - -In the second function this dereferencing is not necessary, because the `map` -function expects the new value to be returned. - -After you've completed both functions, decide for yourself which approach you -like better. - -What do you think is the more commonly used pattern under Rust developers? -""" - -# MOVE SEMANTICS - -[[exercises]] -name = "move_semantics1" -dir = "06_move_semantics" -hint = """ -So you've got the "cannot borrow immutable local variable `vec` as mutable" -error on the line where we push an element to the vector, right? - -The fix for this is going to be adding one keyword, and the addition is NOT on -the line where we push to the vector (where the error is). - -Also: Try accessing `vec0` after having called `fill_vec()`. See what -happens!""" - -[[exercises]] -name = "move_semantics2" -dir = "06_move_semantics" -hint = """ -When running this exercise for the first time, you'll notice an error about -"borrow of moved value". In Rust, when an argument is passed to a function and -it's not explicitly returned, you can't use the original variable anymore. -We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's -being "moved" into `vec1`, meaning we can't access `vec0` anymore after the -fact. - -Rust provides a couple of different ways to mitigate this issue, feel free to -try them all: -1. You could make another, separate version of the data that's in `vec0` and - pass that to `fill_vec` instead. -2. Make `fill_vec` borrow its argument instead of taking ownership of it, - and then copy the data within the function (`vec.clone()`) in order to - return an owned `Vec`. -""" - -[[exercises]] -name = "move_semantics3" -dir = "06_move_semantics" -hint = """ -The difference between this one and the previous ones is that the first line -of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, -instead of adding that line back, add `mut` in one place that will change -an existing binding to be a mutable binding instead of an immutable one :)""" - -[[exercises]] -name = "move_semantics4" -dir = "06_move_semantics" -hint = """ -Stop reading whenever you feel like you have enough direction :) Or try -doing one step and then fixing the compiler errors that result! -So the end goal is to: - - get rid of the first line in main that creates the new vector - - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` - - `fill_vec` has had its signature changed, which our call should reflect - - since we're not creating a new vec in `main` anymore, we need to create - a new vec in `fill_vec`, and fill it with the expected values""" - -[[exercises]] -name = "move_semantics5" -dir = "06_move_semantics" -hint = """ -Carefully reason about the range in which each mutable reference is in -scope. Does it help to update the value of referent (`x`) immediately after -the mutable reference is taken? Read more about 'Mutable References' -in the book's section 'References and Borrowing': -https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. -""" - -[[exercises]] -name = "move_semantics6" -dir = "06_move_semantics" -test = false -hint = """ -To find the answer, you can consult the book section "References and Borrowing": -https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html - -The first problem is that `get_char` is taking ownership of the string. So -`data` is moved and can't be used for `string_uppercase`. `data` is moved to -`get_char` first, meaning that `string_uppercase` cannot manipulate the data. - -Once you've fixed that, `string_uppercase`'s function signature will also need -to be adjusted. - -Can you figure out how? - -Another hint: it has to do with the `&` character.""" - -# STRUCTS - -[[exercises]] -name = "structs1" -dir = "07_structs" -hint = """ -Rust has more than one type of struct. Three actually, all variants are used to -package related data together. - -There are normal (or classic) structs. These are named collections of related -data stored in fields. - -Tuple structs are basically just named tuples. - -Finally, Unit-like structs. These don't have any fields and are useful for -generics. - -In this exercise you need to complete and implement one of each kind. -Read more about structs in The Book: -https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" - -[[exercises]] -name = "structs2" -dir = "07_structs" -hint = """ -Creating instances of structs is easy, all you need to do is assign some values -to its fields. - -There are however some shortcuts that can be taken when instantiating structs. -Have a look in The Book, to find out more: -https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" - -[[exercises]] -name = "structs3" -dir = "07_structs" -hint = """ -For `is_international`: What makes a package international? Seems related to -the places it goes through right? - -For `get_fees`: This method takes an additional argument, is there a field in -the `Package` struct that this relates to? - -Have a look in The Book, to find out more about method implementations: -https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" - -# ENUMS - -[[exercises]] -name = "enums1" -dir = "08_enums" -test = false -hint = "No hints this time ;)" - -[[exercises]] -name = "enums2" -dir = "08_enums" -test = false -hint = """ -You can create enumerations that have different variants with different types -such as no data, anonymous structs, a single string, tuples, ...etc""" - -[[exercises]] -name = "enums3" -dir = "08_enums" -hint = """ -As a first step, you can define enums to compile this code without errors. - -And then create a match expression in `process()`. - -Note that you need to deconstruct some message variants in the match expression -to get value in the variant.""" - -# STRINGS - -[[exercises]] -name = "strings1" -dir = "09_strings" -test = false -hint = """ -The `current_favorite_color` function is currently returning a string slice -with the `'static` lifetime. We know this because the data of the string lives -in our code itself -- it doesn't come from a file or user input or another -program -- so it will live as long as our program lives. - -But it is still a string slice. There's one way to create a `String` by -converting a string slice covered in the Strings chapter of the book, and -another way that uses the `From` trait.""" - -[[exercises]] -name = "strings2" -dir = "09_strings" -test = false -hint = """ -Yes, it would be really easy to fix this by just changing the value bound to -`word` to be a string slice instead of a `String`, wouldn't it?? There is a way -to add one character to the `if` statement, though, that will coerce the -`String` into a string slice. - -Side note: If you're interested in learning about how this kind of reference -conversion works, you can jump ahead in the book and read this part in the -smart pointers chapter: -https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" - -[[exercises]] -name = "strings3" -dir = "09_strings" -hint = """ -There's tons of useful standard library functions for strings. Let's try and use some of them: -https://doc.rust-lang.org/std/string/struct.String.html#method.trim - -For the `compose_me` method: You can either use the `format!` macro, or convert -the string slice into an owned string, which you can then freely extend.""" - -[[exercises]] -name = "strings4" -dir = "09_strings" -test = false -hint = "No hints this time ;)" - -# MODULES - -[[exercises]] -name = "modules1" -dir = "10_modules" -test = false -hint = """ -Everything is private in Rust by default-- but there's a keyword we can use -to make something public! The compiler error should point to the thing that -needs to be public.""" - -[[exercises]] -name = "modules2" -dir = "10_modules" -test = false -hint = """ -The delicious_snacks module is trying to present an external interface that is -different than its internal structure (the `fruits` and `veggies` modules and -associated constants). Complete the `use` statements to fit the uses in main and -find the one keyword missing for both constants. - -Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" - -[[exercises]] -name = "modules3" -dir = "10_modules" -test = false -hint = """ -`UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a -`use` statement for these two to bring them into scope. You can use nested -paths or the glob operator to bring these two in using only one line.""" - -# HASHMAPS - -[[exercises]] -name = "hashmaps1" -dir = "11_hashmaps" -hint = """ -Hint 1: Take a look at the return type of the function to figure out - the type for the `basket`. - -Hint 2: Number of fruits should be at least 5. And you have to put - at least three different types of fruits. -""" - -[[exercises]] -name = "hashmaps2" -dir = "11_hashmaps" -hint = """ -Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. -Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value -""" - -[[exercises]] -name = "hashmaps3" -dir = "11_hashmaps" -hint = """ -Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert - entries corresponding to each team in the scores table. - -Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value - -Hint 2: If there is already an entry for a given key, the value returned by - `entry()` can be updated based on the existing value. - -Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value -""" - -# QUIZ 2 - -[[exercises]] -name = "quiz2" -dir = "quizzes" -hint = "No hints this time ;)" - -# OPTIONS - -[[exercises]] -name = "options1" -dir = "12_options" -hint = """ -Options can have a `Some` value, with an inner value, or a `None` value, -without an inner value. - -There's multiple ways to get at the inner value, you can use `unwrap`, or -pattern match. Unwrapping is the easiest, but how do you do it safely so that -it doesn't panic in your face later?""" - -[[exercises]] -name = "options2" -dir = "12_options" -hint = """ -Check out: - -- https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html -- https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html - -Remember that `Option`s can be stacked in `if let` and `while let`. - -For example: `Some(Some(variable)) = variable2` - -Also see `Option::flatten` -""" - -[[exercises]] -name = "options3" -dir = "12_options" -test = false -hint = """ -The compiler says a partial move happened in the `match` statement. How can -this be avoided? The compiler shows the correction needed. - -After making the correction as suggested by the compiler, do read: -https://doc.rust-lang.org/std/keyword.ref.html""" - -# ERROR HANDLING - -[[exercises]] -name = "errors1" -dir = "13_error_handling" -hint = """ -`Ok` and `Err` are the two variants of `Result`, so what the tests are saying -is that `generate_nametag_text` should return a `Result` instead of an `Option`. - -To make this change, you'll need to: - - update the return type in the function signature to be a `Result` that could be the variants `Ok(String)` and `Err(String)` - - change the body of the function to return `Ok(stuff)` where it currently - returns `Some(stuff)` - - change the body of the function to return `Err(error message)` where it - currently returns `None`""" - -[[exercises]] -name = "errors2" -dir = "13_error_handling" -hint = """ -One way to handle this is using a `match` statement on -`item_quantity.parse::()` where the cases are `Ok(something)` and -`Err(something)`. - -This pattern is very common in Rust, though, so there's a `?` operator that -does pretty much what you would make that match statement do for you! - -Take a look at this section of the 'Error Handling' chapter: -https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator -and give it a try!""" - -[[exercises]] -name = "errors3" -dir = "13_error_handling" -test = false -hint = """ -If other functions can return a `Result`, why shouldn't `main`? It's a fairly -common convention to return something like `Result<(), ErrorType>` from your -main function. - -The unit (`()`) type is there because nothing is really needed in terms of -positive results.""" - -[[exercises]] -name = "errors4" -dir = "13_error_handling" -hint = """ -`PositiveNonzeroInteger::new` is always creating a new instance and returning -an `Ok` result. - -It should be doing some checking, returning an `Err` result if those checks -fail, and only returning an `Ok` result if those checks determine that -everything is... okay :)""" - -[[exercises]] -name = "errors5" -dir = "13_error_handling" -test = false -hint = """ -There are two different possible `Result` types produced within `main()`, which -are propagated using `?` operators. How do we declare a return type from -`main()` that allows both? - -Under the hood, the `?` operator calls `From::from` on the error value to -convert it to a boxed trait object, a `Box`. This boxed trait -object is polymorphic, and since all errors implement the `error::Error` trait, -we can capture lots of different errors in one "Box" object. - -Check out this section of the book: -https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator - -Read more about boxing errors: -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html - -Read more about using the `?` operator with boxed errors: -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html -""" - -[[exercises]] -name = "errors6" -dir = "13_error_handling" -hint = """ -This exercise uses a completed version of `PositiveNonzeroInteger` from -errors4. - -Below the line that `TODO` asks you to change, there is an example of using -the `map_err()` method on a `Result` to transform one type of error into -another. Try using something similar on the `Result` from `parse()`. You -might use the `?` operator to return early from the function, or you might -use a `match` expression, or maybe there's another way! - -You can create another function inside `impl ParsePosNonzeroError` to use -with `map_err()`. - -Read more about `map_err()` in the `std::result` documentation: -https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" - -# Generics - -[[exercises]] -name = "generics1" -dir = "14_generics" -test = false -hint = """ -Vectors in Rust make use of generics to create dynamically sized arrays of any -type. - -You need to tell the compiler what type we are pushing onto this vector.""" - -[[exercises]] -name = "generics2" -dir = "14_generics" -hint = """ -Currently we are wrapping only values of type `u32`. - -Maybe we could update the explicit references to this data type somehow? - -If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions -""" - -# TRAITS - -[[exercises]] -name = "traits1" -dir = "15_traits" -hint = """ -A discussion about Traits in Rust can be found at: -https://doc.rust-lang.org/book/ch10-02-traits.html -""" - -[[exercises]] -name = "traits2" -dir = "15_traits" -hint = """ -Notice how the trait takes ownership of `self`, and returns `Self`. - -Try mutating the incoming string vector. Have a look at the tests to see -what the result should look like! - -Vectors provide suitable methods for adding an element at the end. See -the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" - -[[exercises]] -name = "traits3" -dir = "15_traits" -hint = """ -Traits can have a default implementation for functions. Structs that implement -the trait can then use the default version of these functions if they choose not -to implement the function themselves. - -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations -""" - -[[exercises]] -name = "traits4" -dir = "15_traits" -hint = """ -Instead of using concrete types as parameters you can use traits. Try replacing -the '??' with 'impl ' - -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters -""" - -[[exercises]] -name = "traits5" -dir = "15_traits" -test = false -hint = """ -To ensure a parameter implements multiple traits use the '+ syntax'. Try -replacing the '??' with 'impl <> + <>'. - -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax -""" - -# QUIZ 3 - -[[exercises]] -name = "quiz3" -dir = "quizzes" -hint = """ -To find the best solution to this challenge you're going to need to think back -to your knowledge of traits, specifically 'Trait Bound Syntax' - -You may also need this: `use std::fmt::Display;`.""" - -# LIFETIMES - -[[exercises]] -name = "lifetimes1" -dir = "16_lifetimes" -test = false -hint = """ -Let the compiler guide you. Also take a look at the book if you need help: -https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" - -[[exercises]] -name = "lifetimes2" -dir = "16_lifetimes" -test = false -hint = """ -Remember that the generic lifetime `'a` will get the concrete lifetime that is -equal to the smaller of the lifetimes of `x` and `y`. - -You can take at least two paths to achieve the desired result while keeping the -inner block: -1. Move the `string2` declaration to make it live as long as `string1` (how is - `result` declared?) -2. Move `println!` into the inner block""" - -[[exercises]] -name = "lifetimes3" -dir = "16_lifetimes" -test = false -hint = """ -If you use a lifetime annotation in a struct's fields, where else does it need -to be added?""" - -# TESTS - -[[exercises]] -name = "tests1" -dir = "17_tests" -hint = """ -You don't even need to write any code to test -- you can just test values and -run that, even though you wouldn't do that in real life. :) - -`assert!` is a macro that needs an argument. Depending on the value of the -argument, `assert!` will do nothing (in which case the test will pass) or -`assert!` will panic (in which case the test will fail). - -So try giving different values to `assert!` and see which ones compile, which -ones pass, and which ones fail :)""" - -[[exercises]] -name = "tests2" -dir = "17_tests" -hint = """ -Like the previous exercise, you don't need to write any code to get this test -to compile and run. - -`assert_eq!` is a macro that takes two arguments and compares them. Try giving -it two values that are equal! Try giving it two arguments that are different! -Try giving it two values that are of different types! Try switching which -argument comes first and which comes second!""" - -[[exercises]] -name = "tests3" -dir = "17_tests" -hint = """ -You can call a function right where you're passing arguments to `assert!`. So -you could do something like `assert!(having_fun())`. - -If you want to check that you indeed get `false`, you can negate the result of -what you're doing using `!`, like `assert!(!having_fun())`.""" - -[[exercises]] -name = "tests4" -dir = "17_tests" -hint = """ -We expect method `Rectangle::new()` to panic for negative values. - -To handle that you need to add a special attribute to the test function. - -You can refer to the docs: -https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" - -# STANDARD LIBRARY TYPES - -[[exercises]] -name = "iterators1" -dir = "18_iterators" -hint = """ -Step 1: - -We need to apply something to the collection `my_fav_fruits` before we start to -go through it. What could that be? Take a look at the struct definition for a -vector for inspiration: -https://doc.rust-lang.org/std/vec/struct.Vec.html - -Step 2 & step 3: - -Very similar to the lines above and below. You've got this! - -Step 4: - -An iterator goes through all elements in a collection, but what if we've run -out of elements? What should we expect here? If you're stuck, take a look at -https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. -""" - -[[exercises]] -name = "iterators2" -dir = "18_iterators" -hint = """ -Step 1: - -The variable `first` is a `char`. It needs to be capitalized and added to the -remaining characters in `c` in order to return the correct `String`. - -The remaining characters in `c` can be viewed as a string slice using the -`as_str` method. - -The documentation for `char` contains many useful methods. -https://doc.rust-lang.org/std/primitive.char.html - -Step 2: - -Create an iterator from the slice. Transform the iterated values by applying -the `capitalize_first` function. Remember to `collect` the iterator. - -Step 3: - -This is surprisingly similar to the previous solution. `collect` is very -powerful and very general. Rust just needs to know the desired type.""" - -[[exercises]] -name = "iterators3" -dir = "18_iterators" -hint = """ -The `divide` function needs to return the correct error when even division is -not possible. - -The `division_results` variable needs to be collected into a collection type. - -The `result_with_list` function needs to return a single `Result` where the -success case is a vector of integers and the failure case is a `DivisionError`. - -The `list_of_results` function needs to return a vector of results. - -See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for -how the `FromIterator` trait is used in `collect()`. This trait is REALLY -powerful! It can make the solution to this exercise infinitely easier.""" - -[[exercises]] -name = "iterators4" -dir = "18_iterators" -hint = """ -In an imperative language, you might write a `for` loop that updates a mutable -variable. Or, you might write code utilizing recursion and a match clause. In -Rust you can take another functional approach, computing the factorial -elegantly with ranges and iterators. - -Hint 2: Check out the `fold` and `rfold` methods!""" - -[[exercises]] -name = "iterators5" -dir = "18_iterators" -hint = """ -The documentation for the `std::iter::Iterator` trait contains numerous methods -that would be helpful here. - -The `collection` variable in `count_collection_iterator` is a slice of -`HashMap`s. It needs to be converted into an iterator in order to use the -iterator methods. - -The `fold` method can be useful in the `count_collection_iterator` function. - -For a further challenge, consult the documentation for `Iterator` to find -a different method that could make your code more compact than using `fold`.""" - -# SMART POINTERS - -[[exercises]] -name = "box1" -dir = "19_smart_pointers" -hint = """ -Step 1: - -The compiler's message should help: since we cannot store the value of the -actual type when working with recursive types, we need to store a reference -(pointer) to its value. - -We should, therefore, place our `List` inside a `Box`. More details in the book -here: https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes - -Step 2: - -Creating an empty list should be fairly straightforward (hint: peek at the -assertions). - -For a non-empty list keep in mind that we want to use our `Cons` "list builder". -Although the current list is one of integers (`i32`), feel free to change the -definition and try other types! -""" - -[[exercises]] -name = "rc1" -dir = "19_smart_pointers" -hint = """ -This is a straightforward exercise to use the `Rc` type. Each `Planet` has -ownership of the `Sun`, and uses `Rc::clone()` to increment the reference count -of the `Sun`. - -After using `drop()` to move the `Planet`s out of scope individually, the -reference count goes down. - -In the end the `Sun` only has one reference again, to itself. - -See more at: https://doc.rust-lang.org/book/ch15-04-rc.html - -* Unfortunately Pluto is no longer considered a planet :( -""" - -[[exercises]] -name = "arc1" -dir = "19_smart_pointers" -test = false -hint = """ -Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order -to avoid creating a copy of `numbers`, you'll need to create `child_numbers` -inside the loop but still in the main thread. - -`child_numbers` should be a clone of the `Arc` of the numbers instead of a -thread-local copy of the numbers. - -This is a simple exercise if you understand the underlying concepts, but if this -is too much of a struggle, consider reading through all of Chapter 16 in the -book: -https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html -""" - -[[exercises]] -name = "cow1" -dir = "19_smart_pointers" -hint = """ -If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is -called. - -Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation -on the `Cow` type. -""" - -# THREADS - -[[exercises]] -name = "threads1" -dir = "20_threads" -test = false -hint = """ -`JoinHandle` is a struct that is returned from a spawned thread: -https://doc.rust-lang.org/std/thread/fn.spawn.html - -A challenge with multi-threaded applications is that the main thread can -finish before the spawned threads are completed. -https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles - -Use the `JoinHandle`s to wait for each thread to finish and collect their -results. - -https://doc.rust-lang.org/std/thread/struct.JoinHandle.html -""" - -[[exercises]] -name = "threads2" -dir = "20_threads" -test = false -hint = """ -`Arc` is an Atomic Reference Counted pointer that allows safe, shared access -to **immutable** data. But we want to *change* the number of `jobs_completed` -so we'll need to also use another type that will only allow one thread to -mutate the data at a time. Take a look at this section of the book: -https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct - -Keep reading if you'd like more hints :) - -Do you now have an `Arc>` at the beginning of `main`? Like: -``` -let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); -``` - -Similar to the code in the following example in the book: -https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads -""" - -[[exercises]] -name = "threads3" -dir = "20_threads" -hint = """ -An alternate way to handle concurrency between threads is to use an `mpsc` -(multiple producer, single consumer) channel to communicate. - -With both a sending end and a receiving end, it's possible to send values in -one thread and receive them in another. - -Multiple producers are possible by using clone() to create a duplicate of the -original sending end. - -See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. -""" - -# MACROS - -[[exercises]] -name = "macros1" -dir = "21_macros" -test = false -hint = """ -When you call a macro, you need to add something special compared to a -regular function call. If you're stuck, take a look at what's inside -`my_macro`.""" - -[[exercises]] -name = "macros2" -dir = "21_macros" -test = false -hint = """ -Macros don't quite play by the same rules as the rest of Rust, in terms of -what's available where. - -Unlike other things in Rust, the order of "where you define a macro" versus -"where you use it" actually matters.""" - -[[exercises]] -name = "macros3" -dir = "21_macros" -test = false -hint = """ -In order to use a macro outside of its module, you need to do something -special to the module to lift the macro out into its parent. - -The same trick also works on "extern crate" statements for crates that have -exported macros, if you've seen any of those around.""" - -[[exercises]] -name = "macros4" -dir = "21_macros" -test = false -hint = """ -You only need to add a single character to make this compile. - -The way macros are written, it wants to see something between each "macro arm", -so it can separate them. - -That's all the macro exercises we have in here, but it's barely even scratching -the surface of what you can do with Rust's macros. For a more thorough -introduction, you can have a read through 'The Little Book of Rust Macros': -https://veykril.github.io/tlborm/""" - -# CLIPPY - -[[exercises]] -name = "clippy1" -dir = "22_clippy" -test = false -strict_clippy = true -hint = """ -Rust stores the highest precision version of any long or infinite precision -mathematical constants in the Rust standard library: -https://doc.rust-lang.org/stable/std/f32/consts/index.html - -We may be tempted to use our own approximations for certain mathematical -constants, but clippy recognizes those imprecise mathematical constants as a -source of potential error. - -See the suggestions of the clippy warning in compile output and use the -appropriate replacement constant from `std::f32::consts`...""" - -[[exercises]] -name = "clippy2" -dir = "22_clippy" -test = false -strict_clippy = true -hint = """ -`for` loops over `Option` values are more clearly expressed as an `if let`""" - -[[exercises]] -name = "clippy3" -dir = "22_clippy" -test = false -strict_clippy = true -hint = "No hints this time!" - -# TYPE CONVERSIONS - -[[exercises]] -name = "using_as" -dir = "23_conversions" -hint = """ -Use the `as` operator to cast one of the operands in the last line of the -`average` function into the expected return type.""" - -[[exercises]] -name = "from_into" -dir = "23_conversions" -hint = """ -Follow the steps provided right before the `From` implementation""" - -[[exercises]] -name = "from_str" -dir = "23_conversions" -hint = """ -The implementation of `FromStr` should return an `Ok` with a `Person` object, -or an `Err` with an error if the string is not valid. - -This is almost like the `from_into` exercise, but returning errors instead -of falling back to a default value. - -Look at the test cases to see which error variants to return. - -Another hint: You can use the `map_err` method of `Result` with a function -or a closure to wrap the error from `parse::`. - -Yet another hint: If you would like to propagate errors by using the `?` -operator in your solution, you might want to look at -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html -""" - -[[exercises]] -name = "try_from_into" -dir = "23_conversions" -hint = """ -Follow the steps provided right before the `TryFrom` implementation. -You can also use the example at -https://doc.rust-lang.org/std/convert/trait.TryFrom.html - -Is there an implementation of `TryFrom` in the standard library that -can both do the required integer conversion and check the range of the input? - -Another hint: Look at the test cases to see which error variants to return. - -Yet another hint: You can use the `map_err` or `or` methods of `Result` to -convert errors. - -Yet another hint: If you would like to propagate errors by using the `?` -operator in your solution, you might want to look at -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html - -Challenge: Can you make the `TryFrom` implementations generic over many integer types?""" - -[[exercises]] -name = "as_ref_mut" -dir = "23_conversions" -hint = """ -Add `AsRef` or `AsMut` as a trait bound to the functions.""" diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml deleted file mode 120000 index 3795291..0000000 --- a/rustlings-macros/info.toml +++ /dev/null @@ -1 +0,0 @@ -../info.toml \ No newline at end of file diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml new file mode 100644 index 0000000..4204f27 --- /dev/null +++ b/rustlings-macros/info.toml @@ -0,0 +1,1286 @@ +format_version = 1 + +welcome_message = """Is this your first time? Don't worry, Rustlings is made for beginners! +We are going to teach you a lot of things about Rust, but before we can +get started, here are some notes about how Rustlings operates: + +1. The central concept behind Rustlings is that you solve exercises. These + exercises usually contain some compiler or logic errors which cause the + exercise to fail compilation or testing. It's your job to find all errors + and fix them! +2. Make sure to have your editor open in the `rustlings/` directory. Rustlings + will show you the path of the current exercise under the progress bar. Open + the exercise file in your editor, fix errors and save the file. Rustlings will + automatically detect the file change and rerun the exercise. If all errors are + fixed, Rustlings will ask you to move on to the next exercise. +3. If you're stuck on an exercise, enter `h` (or `hint`) to show a hint. +4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! + (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, + other learners do too so you can help each other out! +""" + +final_message = """We hope you enjoyed learning about the various aspects of Rust! +If you noticed any issues, don't hesitate to report them on Github. +You can also contribute your own exercises to help the greater community! + +Before reporting an issue or contributing, please read our guidelines: +https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md +""" + +# INTRO + +# TODO: Update exercise +[[exercises]] +name = "intro1" +dir = "00_intro" +test = false +# TODO: Fix hint +hint = """Enter `n` (or `next`) followed by ENTER to move on to the next exercise""" + +[[exercises]] +name = "intro2" +dir = "00_intro" +test = false +hint = """ +The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" + +# VARIABLES + +[[exercises]] +name = "variables1" +dir = "01_variables" +test = false +hint = """ +The declaration in the first line in the main function is missing a keyword +that is needed in Rust to create a new variable binding.""" + +[[exercises]] +name = "variables2" +dir = "01_variables" +test = false +hint = """ +The compiler message is saying that Rust cannot infer the type that the +variable binding `x` has with what is given here. + +What happens if you annotate the first line in the main function with a type +annotation? + +What if you give `x` a value? + +What if you do both? + +What type should `x` be, anyway? + +What if `x` is the same type as `10`? What if it's a different type?""" + +[[exercises]] +name = "variables3" +dir = "01_variables" +test = false +hint = """ +Oops! In this exercise, we have a variable binding that we've created on in the +first line in the `main` function, and we're trying to use it in the next line, +but we haven't given it a value. + +We can't print out something that isn't there; try giving `x` a value! + +This is an error that can cause bugs that's very easy to make in any +programming language -- thankfully the Rust compiler has caught this for us!""" + +[[exercises]] +name = "variables4" +dir = "01_variables" +test = false +hint = """ +In Rust, variable bindings are immutable by default. But here we're trying +to reassign a different value to `x`! There's a keyword we can use to make +a variable binding mutable instead.""" + +[[exercises]] +name = "variables5" +dir = "01_variables" +test = false +hint = """ +In `variables4` we already learned how to make an immutable variable mutable +using a special keyword. Unfortunately this doesn't help us much in this +exercise because we want to assign a different typed value to an existing +variable. Sometimes you may also like to reuse existing variable names because +you are just converting values to different types like in this exercise. + +Fortunately Rust has a powerful solution to this problem: 'Shadowing'! +You can read more about 'Shadowing' in the book's section 'Variables and +Mutability': +https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing + +Try to solve this exercise afterwards using this technique.""" + +[[exercises]] +name = "variables6" +dir = "01_variables" +test = false +hint = """ +We know about variables and mutability, but there is another important type of +variable available: constants. + +Constants are always immutable and they are declared with keyword `const` rather +than keyword `let`. + +Constants types must also always be annotated. + +Read more about constants and the differences between variables and constants +under 'Constants' in the book's section 'Variables and Mutability': +https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants +""" + +# FUNCTIONS + +[[exercises]] +name = "functions1" +dir = "02_functions" +test = false +hint = """ +This main function is calling a function that it expects to exist, but the +function doesn't exist. It expects this function to have the name `call_me`. +It expects this function to not take any arguments and not return a value. +Sounds a lot like `main`, doesn't it?""" + +[[exercises]] +name = "functions2" +dir = "02_functions" +test = false +hint = """ +Rust requires that all parts of a function's signature have type annotations, +but `call_me` is missing the type annotation of `num`.""" + +[[exercises]] +name = "functions3" +dir = "02_functions" +test = false +hint = """ +This time, the function *declaration* is okay, but there's something wrong +with the place where we're calling the function.""" + +[[exercises]] +name = "functions4" +dir = "02_functions" +test = false +hint = """ +The error message points to the function `sale_price` and says it expects a type +after the `->`. This is where the function's return type should be -- take a +look at the `is_even` function for an example!""" + +[[exercises]] +name = "functions5" +dir = "02_functions" +test = false +hint = """ +This is a really common error that can be fixed by removing one character. +It happens because Rust distinguishes between expressions and statements: +expressions return a value based on their operand(s), and statements simply +return a `()` type which behaves just like `void` in C/C++ language. + +We want to return a value of `i32` type from the `square` function, but it is +returning a `()` type... + +They are not the same. There are two solutions: +1. Add a `return` ahead of `num * num;` +2. remove `;`, make it to be `num * num`""" + +# IF + +[[exercises]] +name = "if1" +dir = "03_if" +hint = """ +It's possible to do this in one line if you would like! + +Some similar examples from other languages: +- In C(++) this would be: `a > b ? a : b` +- In Python this would be: `a if a > b else b` + +Remember in Rust that: +- the `if` condition does not need to be surrounded by parentheses +- `if`/`else` conditionals are expressions +- Each condition is followed by a `{}` block.""" + +[[exercises]] +name = "if2" +dir = "03_if" +hint = """ +For that first compiler error, it's important in Rust that each conditional +block returns the same type! To get the tests passing, you will need a couple +conditions checking different input values.""" + +[[exercises]] +name = "if3" +dir = "03_if" +hint = """ +In Rust, every arm of an `if` expression has to return the same type of value. +Make sure the type is consistent across all arms.""" + +# QUIZ 1 + +[[exercises]] +name = "quiz1" +dir = "quizzes" +hint = "No hints this time ;)" + +# PRIMITIVE TYPES + +[[exercises]] +name = "primitive_types1" +dir = "04_primitive_types" +test = false +hint = "No hints this time ;)" + +[[exercises]] +name = "primitive_types2" +dir = "04_primitive_types" +test = false +hint = "No hints this time ;)" + +[[exercises]] +name = "primitive_types3" +dir = "04_primitive_types" +test = false +hint = """ +There's a shorthand to initialize Arrays with a certain size that does not +require you to type in 100 items (but you certainly can if you want!). + +For example, you can do: +``` +let array = ["Are we there yet?"; 10]; +``` + +Bonus: what are some other things you could have that would return `true` +for `a.len() >= 100`?""" + +[[exercises]] +name = "primitive_types4" +dir = "04_primitive_types" +hint = """ +Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section +of the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the +starting and ending (plus one) indices of the items in the `Array` that you +want to end up in the slice. + +If you're curious why the first argument of `assert_eq!` does not have an +ampersand for a reference since the second argument is a reference, take a look +at the coercion chapter of the nomicon: +https://doc.rust-lang.org/nomicon/coercions.html""" + +[[exercises]] +name = "primitive_types5" +dir = "04_primitive_types" +test = false +hint = """ +Take a look at the 'Data Types -> The Tuple Type' section of the book: +https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type +Particularly the part about destructuring (second to last example in the +section). + +You'll need to make a pattern to bind `name` and `age` to the appropriate parts +of the tuple. You can do it!!""" + +[[exercises]] +name = "primitive_types6" +dir = "04_primitive_types" +hint = """ +While you could use a destructuring `let` for the tuple here, try +indexing into it instead, as explained in the last example of the +'Data Types -> The Tuple Type' section of the book: +https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type +Now you have another tool in your toolbox!""" + +# VECS + +[[exercises]] +name = "vecs1" +dir = "05_vecs" +hint = """ +In Rust, there are two ways to define a Vector. +1. One way is to use the `Vec::new()` function to create a new vector + and fill it with the `push()` method. +2. The second way, which is simpler is to use the `vec![]` macro and + define your elements inside the square brackets. + +Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html +of the Rust book to learn more. +""" + +[[exercises]] +name = "vecs2" +dir = "05_vecs" +hint = """ +In the first function we are looping over the Vector and getting a reference to +one `element` at a time. + +To modify the value of that `element` we need to use the `*` dereference +operator. You can learn more in this chapter of the Rust book: +https://doc.rust-lang.org/stable/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector + +In the second function this dereferencing is not necessary, because the `map` +function expects the new value to be returned. + +After you've completed both functions, decide for yourself which approach you +like better. + +What do you think is the more commonly used pattern under Rust developers? +""" + +# MOVE SEMANTICS + +[[exercises]] +name = "move_semantics1" +dir = "06_move_semantics" +hint = """ +So you've got the "cannot borrow immutable local variable `vec` as mutable" +error on the line where we push an element to the vector, right? + +The fix for this is going to be adding one keyword, and the addition is NOT on +the line where we push to the vector (where the error is). + +Also: Try accessing `vec0` after having called `fill_vec()`. See what +happens!""" + +[[exercises]] +name = "move_semantics2" +dir = "06_move_semantics" +hint = """ +When running this exercise for the first time, you'll notice an error about +"borrow of moved value". In Rust, when an argument is passed to a function and +it's not explicitly returned, you can't use the original variable anymore. +We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's +being "moved" into `vec1`, meaning we can't access `vec0` anymore after the +fact. + +Rust provides a couple of different ways to mitigate this issue, feel free to +try them all: +1. You could make another, separate version of the data that's in `vec0` and + pass that to `fill_vec` instead. +2. Make `fill_vec` borrow its argument instead of taking ownership of it, + and then copy the data within the function (`vec.clone()`) in order to + return an owned `Vec`. +""" + +[[exercises]] +name = "move_semantics3" +dir = "06_move_semantics" +hint = """ +The difference between this one and the previous ones is that the first line +of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, +instead of adding that line back, add `mut` in one place that will change +an existing binding to be a mutable binding instead of an immutable one :)""" + +[[exercises]] +name = "move_semantics4" +dir = "06_move_semantics" +hint = """ +Stop reading whenever you feel like you have enough direction :) Or try +doing one step and then fixing the compiler errors that result! +So the end goal is to: + - get rid of the first line in main that creates the new vector + - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` + - `fill_vec` has had its signature changed, which our call should reflect + - since we're not creating a new vec in `main` anymore, we need to create + a new vec in `fill_vec`, and fill it with the expected values""" + +[[exercises]] +name = "move_semantics5" +dir = "06_move_semantics" +hint = """ +Carefully reason about the range in which each mutable reference is in +scope. Does it help to update the value of referent (`x`) immediately after +the mutable reference is taken? Read more about 'Mutable References' +in the book's section 'References and Borrowing': +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. +""" + +[[exercises]] +name = "move_semantics6" +dir = "06_move_semantics" +test = false +hint = """ +To find the answer, you can consult the book section "References and Borrowing": +https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html + +The first problem is that `get_char` is taking ownership of the string. So +`data` is moved and can't be used for `string_uppercase`. `data` is moved to +`get_char` first, meaning that `string_uppercase` cannot manipulate the data. + +Once you've fixed that, `string_uppercase`'s function signature will also need +to be adjusted. + +Can you figure out how? + +Another hint: it has to do with the `&` character.""" + +# STRUCTS + +[[exercises]] +name = "structs1" +dir = "07_structs" +hint = """ +Rust has more than one type of struct. Three actually, all variants are used to +package related data together. + +There are normal (or classic) structs. These are named collections of related +data stored in fields. + +Tuple structs are basically just named tuples. + +Finally, Unit-like structs. These don't have any fields and are useful for +generics. + +In this exercise you need to complete and implement one of each kind. +Read more about structs in The Book: +https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" + +[[exercises]] +name = "structs2" +dir = "07_structs" +hint = """ +Creating instances of structs is easy, all you need to do is assign some values +to its fields. + +There are however some shortcuts that can be taken when instantiating structs. +Have a look in The Book, to find out more: +https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" + +[[exercises]] +name = "structs3" +dir = "07_structs" +hint = """ +For `is_international`: What makes a package international? Seems related to +the places it goes through right? + +For `get_fees`: This method takes an additional argument, is there a field in +the `Package` struct that this relates to? + +Have a look in The Book, to find out more about method implementations: +https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" + +# ENUMS + +[[exercises]] +name = "enums1" +dir = "08_enums" +test = false +hint = "No hints this time ;)" + +[[exercises]] +name = "enums2" +dir = "08_enums" +test = false +hint = """ +You can create enumerations that have different variants with different types +such as no data, anonymous structs, a single string, tuples, ...etc""" + +[[exercises]] +name = "enums3" +dir = "08_enums" +hint = """ +As a first step, you can define enums to compile this code without errors. + +And then create a match expression in `process()`. + +Note that you need to deconstruct some message variants in the match expression +to get value in the variant.""" + +# STRINGS + +[[exercises]] +name = "strings1" +dir = "09_strings" +test = false +hint = """ +The `current_favorite_color` function is currently returning a string slice +with the `'static` lifetime. We know this because the data of the string lives +in our code itself -- it doesn't come from a file or user input or another +program -- so it will live as long as our program lives. + +But it is still a string slice. There's one way to create a `String` by +converting a string slice covered in the Strings chapter of the book, and +another way that uses the `From` trait.""" + +[[exercises]] +name = "strings2" +dir = "09_strings" +test = false +hint = """ +Yes, it would be really easy to fix this by just changing the value bound to +`word` to be a string slice instead of a `String`, wouldn't it?? There is a way +to add one character to the `if` statement, though, that will coerce the +`String` into a string slice. + +Side note: If you're interested in learning about how this kind of reference +conversion works, you can jump ahead in the book and read this part in the +smart pointers chapter: +https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" + +[[exercises]] +name = "strings3" +dir = "09_strings" +hint = """ +There's tons of useful standard library functions for strings. Let's try and use some of them: +https://doc.rust-lang.org/std/string/struct.String.html#method.trim + +For the `compose_me` method: You can either use the `format!` macro, or convert +the string slice into an owned string, which you can then freely extend.""" + +[[exercises]] +name = "strings4" +dir = "09_strings" +test = false +hint = "No hints this time ;)" + +# MODULES + +[[exercises]] +name = "modules1" +dir = "10_modules" +test = false +hint = """ +Everything is private in Rust by default-- but there's a keyword we can use +to make something public! The compiler error should point to the thing that +needs to be public.""" + +[[exercises]] +name = "modules2" +dir = "10_modules" +test = false +hint = """ +The delicious_snacks module is trying to present an external interface that is +different than its internal structure (the `fruits` and `veggies` modules and +associated constants). Complete the `use` statements to fit the uses in main and +find the one keyword missing for both constants. + +Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" + +[[exercises]] +name = "modules3" +dir = "10_modules" +test = false +hint = """ +`UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a +`use` statement for these two to bring them into scope. You can use nested +paths or the glob operator to bring these two in using only one line.""" + +# HASHMAPS + +[[exercises]] +name = "hashmaps1" +dir = "11_hashmaps" +hint = """ +Hint 1: Take a look at the return type of the function to figure out + the type for the `basket`. + +Hint 2: Number of fruits should be at least 5. And you have to put + at least three different types of fruits. +""" + +[[exercises]] +name = "hashmaps2" +dir = "11_hashmaps" +hint = """ +Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. +Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +""" + +[[exercises]] +name = "hashmaps3" +dir = "11_hashmaps" +hint = """ +Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert + entries corresponding to each team in the scores table. + +Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value + +Hint 2: If there is already an entry for a given key, the value returned by + `entry()` can be updated based on the existing value. + +Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value +""" + +# QUIZ 2 + +[[exercises]] +name = "quiz2" +dir = "quizzes" +hint = "No hints this time ;)" + +# OPTIONS + +[[exercises]] +name = "options1" +dir = "12_options" +hint = """ +Options can have a `Some` value, with an inner value, or a `None` value, +without an inner value. + +There's multiple ways to get at the inner value, you can use `unwrap`, or +pattern match. Unwrapping is the easiest, but how do you do it safely so that +it doesn't panic in your face later?""" + +[[exercises]] +name = "options2" +dir = "12_options" +hint = """ +Check out: + +- https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html +- https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html + +Remember that `Option`s can be stacked in `if let` and `while let`. + +For example: `Some(Some(variable)) = variable2` + +Also see `Option::flatten` +""" + +[[exercises]] +name = "options3" +dir = "12_options" +test = false +hint = """ +The compiler says a partial move happened in the `match` statement. How can +this be avoided? The compiler shows the correction needed. + +After making the correction as suggested by the compiler, do read: +https://doc.rust-lang.org/std/keyword.ref.html""" + +# ERROR HANDLING + +[[exercises]] +name = "errors1" +dir = "13_error_handling" +hint = """ +`Ok` and `Err` are the two variants of `Result`, so what the tests are saying +is that `generate_nametag_text` should return a `Result` instead of an `Option`. + +To make this change, you'll need to: + - update the return type in the function signature to be a `Result` that could be the variants `Ok(String)` and `Err(String)` + - change the body of the function to return `Ok(stuff)` where it currently + returns `Some(stuff)` + - change the body of the function to return `Err(error message)` where it + currently returns `None`""" + +[[exercises]] +name = "errors2" +dir = "13_error_handling" +hint = """ +One way to handle this is using a `match` statement on +`item_quantity.parse::()` where the cases are `Ok(something)` and +`Err(something)`. + +This pattern is very common in Rust, though, so there's a `?` operator that +does pretty much what you would make that match statement do for you! + +Take a look at this section of the 'Error Handling' chapter: +https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator +and give it a try!""" + +[[exercises]] +name = "errors3" +dir = "13_error_handling" +test = false +hint = """ +If other functions can return a `Result`, why shouldn't `main`? It's a fairly +common convention to return something like `Result<(), ErrorType>` from your +main function. + +The unit (`()`) type is there because nothing is really needed in terms of +positive results.""" + +[[exercises]] +name = "errors4" +dir = "13_error_handling" +hint = """ +`PositiveNonzeroInteger::new` is always creating a new instance and returning +an `Ok` result. + +It should be doing some checking, returning an `Err` result if those checks +fail, and only returning an `Ok` result if those checks determine that +everything is... okay :)""" + +[[exercises]] +name = "errors5" +dir = "13_error_handling" +test = false +hint = """ +There are two different possible `Result` types produced within `main()`, which +are propagated using `?` operators. How do we declare a return type from +`main()` that allows both? + +Under the hood, the `?` operator calls `From::from` on the error value to +convert it to a boxed trait object, a `Box`. This boxed trait +object is polymorphic, and since all errors implement the `error::Error` trait, +we can capture lots of different errors in one "Box" object. + +Check out this section of the book: +https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator + +Read more about boxing errors: +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html + +Read more about using the `?` operator with boxed errors: +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html +""" + +[[exercises]] +name = "errors6" +dir = "13_error_handling" +hint = """ +This exercise uses a completed version of `PositiveNonzeroInteger` from +errors4. + +Below the line that `TODO` asks you to change, there is an example of using +the `map_err()` method on a `Result` to transform one type of error into +another. Try using something similar on the `Result` from `parse()`. You +might use the `?` operator to return early from the function, or you might +use a `match` expression, or maybe there's another way! + +You can create another function inside `impl ParsePosNonzeroError` to use +with `map_err()`. + +Read more about `map_err()` in the `std::result` documentation: +https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" + +# Generics + +[[exercises]] +name = "generics1" +dir = "14_generics" +test = false +hint = """ +Vectors in Rust make use of generics to create dynamically sized arrays of any +type. + +You need to tell the compiler what type we are pushing onto this vector.""" + +[[exercises]] +name = "generics2" +dir = "14_generics" +hint = """ +Currently we are wrapping only values of type `u32`. + +Maybe we could update the explicit references to this data type somehow? + +If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions +""" + +# TRAITS + +[[exercises]] +name = "traits1" +dir = "15_traits" +hint = """ +A discussion about Traits in Rust can be found at: +https://doc.rust-lang.org/book/ch10-02-traits.html +""" + +[[exercises]] +name = "traits2" +dir = "15_traits" +hint = """ +Notice how the trait takes ownership of `self`, and returns `Self`. + +Try mutating the incoming string vector. Have a look at the tests to see +what the result should look like! + +Vectors provide suitable methods for adding an element at the end. See +the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" + +[[exercises]] +name = "traits3" +dir = "15_traits" +hint = """ +Traits can have a default implementation for functions. Structs that implement +the trait can then use the default version of these functions if they choose not +to implement the function themselves. + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations +""" + +[[exercises]] +name = "traits4" +dir = "15_traits" +hint = """ +Instead of using concrete types as parameters you can use traits. Try replacing +the '??' with 'impl ' + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters +""" + +[[exercises]] +name = "traits5" +dir = "15_traits" +test = false +hint = """ +To ensure a parameter implements multiple traits use the '+ syntax'. Try +replacing the '??' with 'impl <> + <>'. + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax +""" + +# QUIZ 3 + +[[exercises]] +name = "quiz3" +dir = "quizzes" +hint = """ +To find the best solution to this challenge you're going to need to think back +to your knowledge of traits, specifically 'Trait Bound Syntax' + +You may also need this: `use std::fmt::Display;`.""" + +# LIFETIMES + +[[exercises]] +name = "lifetimes1" +dir = "16_lifetimes" +test = false +hint = """ +Let the compiler guide you. Also take a look at the book if you need help: +https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" + +[[exercises]] +name = "lifetimes2" +dir = "16_lifetimes" +test = false +hint = """ +Remember that the generic lifetime `'a` will get the concrete lifetime that is +equal to the smaller of the lifetimes of `x` and `y`. + +You can take at least two paths to achieve the desired result while keeping the +inner block: +1. Move the `string2` declaration to make it live as long as `string1` (how is + `result` declared?) +2. Move `println!` into the inner block""" + +[[exercises]] +name = "lifetimes3" +dir = "16_lifetimes" +test = false +hint = """ +If you use a lifetime annotation in a struct's fields, where else does it need +to be added?""" + +# TESTS + +[[exercises]] +name = "tests1" +dir = "17_tests" +hint = """ +You don't even need to write any code to test -- you can just test values and +run that, even though you wouldn't do that in real life. :) + +`assert!` is a macro that needs an argument. Depending on the value of the +argument, `assert!` will do nothing (in which case the test will pass) or +`assert!` will panic (in which case the test will fail). + +So try giving different values to `assert!` and see which ones compile, which +ones pass, and which ones fail :)""" + +[[exercises]] +name = "tests2" +dir = "17_tests" +hint = """ +Like the previous exercise, you don't need to write any code to get this test +to compile and run. + +`assert_eq!` is a macro that takes two arguments and compares them. Try giving +it two values that are equal! Try giving it two arguments that are different! +Try giving it two values that are of different types! Try switching which +argument comes first and which comes second!""" + +[[exercises]] +name = "tests3" +dir = "17_tests" +hint = """ +You can call a function right where you're passing arguments to `assert!`. So +you could do something like `assert!(having_fun())`. + +If you want to check that you indeed get `false`, you can negate the result of +what you're doing using `!`, like `assert!(!having_fun())`.""" + +[[exercises]] +name = "tests4" +dir = "17_tests" +hint = """ +We expect method `Rectangle::new()` to panic for negative values. + +To handle that you need to add a special attribute to the test function. + +You can refer to the docs: +https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" + +# STANDARD LIBRARY TYPES + +[[exercises]] +name = "iterators1" +dir = "18_iterators" +hint = """ +Step 1: + +We need to apply something to the collection `my_fav_fruits` before we start to +go through it. What could that be? Take a look at the struct definition for a +vector for inspiration: +https://doc.rust-lang.org/std/vec/struct.Vec.html + +Step 2 & step 3: + +Very similar to the lines above and below. You've got this! + +Step 4: + +An iterator goes through all elements in a collection, but what if we've run +out of elements? What should we expect here? If you're stuck, take a look at +https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. +""" + +[[exercises]] +name = "iterators2" +dir = "18_iterators" +hint = """ +Step 1: + +The variable `first` is a `char`. It needs to be capitalized and added to the +remaining characters in `c` in order to return the correct `String`. + +The remaining characters in `c` can be viewed as a string slice using the +`as_str` method. + +The documentation for `char` contains many useful methods. +https://doc.rust-lang.org/std/primitive.char.html + +Step 2: + +Create an iterator from the slice. Transform the iterated values by applying +the `capitalize_first` function. Remember to `collect` the iterator. + +Step 3: + +This is surprisingly similar to the previous solution. `collect` is very +powerful and very general. Rust just needs to know the desired type.""" + +[[exercises]] +name = "iterators3" +dir = "18_iterators" +hint = """ +The `divide` function needs to return the correct error when even division is +not possible. + +The `division_results` variable needs to be collected into a collection type. + +The `result_with_list` function needs to return a single `Result` where the +success case is a vector of integers and the failure case is a `DivisionError`. + +The `list_of_results` function needs to return a vector of results. + +See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for +how the `FromIterator` trait is used in `collect()`. This trait is REALLY +powerful! It can make the solution to this exercise infinitely easier.""" + +[[exercises]] +name = "iterators4" +dir = "18_iterators" +hint = """ +In an imperative language, you might write a `for` loop that updates a mutable +variable. Or, you might write code utilizing recursion and a match clause. In +Rust you can take another functional approach, computing the factorial +elegantly with ranges and iterators. + +Hint 2: Check out the `fold` and `rfold` methods!""" + +[[exercises]] +name = "iterators5" +dir = "18_iterators" +hint = """ +The documentation for the `std::iter::Iterator` trait contains numerous methods +that would be helpful here. + +The `collection` variable in `count_collection_iterator` is a slice of +`HashMap`s. It needs to be converted into an iterator in order to use the +iterator methods. + +The `fold` method can be useful in the `count_collection_iterator` function. + +For a further challenge, consult the documentation for `Iterator` to find +a different method that could make your code more compact than using `fold`.""" + +# SMART POINTERS + +[[exercises]] +name = "box1" +dir = "19_smart_pointers" +hint = """ +Step 1: + +The compiler's message should help: since we cannot store the value of the +actual type when working with recursive types, we need to store a reference +(pointer) to its value. + +We should, therefore, place our `List` inside a `Box`. More details in the book +here: https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes + +Step 2: + +Creating an empty list should be fairly straightforward (hint: peek at the +assertions). + +For a non-empty list keep in mind that we want to use our `Cons` "list builder". +Although the current list is one of integers (`i32`), feel free to change the +definition and try other types! +""" + +[[exercises]] +name = "rc1" +dir = "19_smart_pointers" +hint = """ +This is a straightforward exercise to use the `Rc` type. Each `Planet` has +ownership of the `Sun`, and uses `Rc::clone()` to increment the reference count +of the `Sun`. + +After using `drop()` to move the `Planet`s out of scope individually, the +reference count goes down. + +In the end the `Sun` only has one reference again, to itself. + +See more at: https://doc.rust-lang.org/book/ch15-04-rc.html + +* Unfortunately Pluto is no longer considered a planet :( +""" + +[[exercises]] +name = "arc1" +dir = "19_smart_pointers" +test = false +hint = """ +Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order +to avoid creating a copy of `numbers`, you'll need to create `child_numbers` +inside the loop but still in the main thread. + +`child_numbers` should be a clone of the `Arc` of the numbers instead of a +thread-local copy of the numbers. + +This is a simple exercise if you understand the underlying concepts, but if this +is too much of a struggle, consider reading through all of Chapter 16 in the +book: +https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html +""" + +[[exercises]] +name = "cow1" +dir = "19_smart_pointers" +hint = """ +If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is +called. + +Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation +on the `Cow` type. +""" + +# THREADS + +[[exercises]] +name = "threads1" +dir = "20_threads" +test = false +hint = """ +`JoinHandle` is a struct that is returned from a spawned thread: +https://doc.rust-lang.org/std/thread/fn.spawn.html + +A challenge with multi-threaded applications is that the main thread can +finish before the spawned threads are completed. +https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles + +Use the `JoinHandle`s to wait for each thread to finish and collect their +results. + +https://doc.rust-lang.org/std/thread/struct.JoinHandle.html +""" + +[[exercises]] +name = "threads2" +dir = "20_threads" +test = false +hint = """ +`Arc` is an Atomic Reference Counted pointer that allows safe, shared access +to **immutable** data. But we want to *change* the number of `jobs_completed` +so we'll need to also use another type that will only allow one thread to +mutate the data at a time. Take a look at this section of the book: +https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct + +Keep reading if you'd like more hints :) + +Do you now have an `Arc>` at the beginning of `main`? Like: +``` +let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); +``` + +Similar to the code in the following example in the book: +https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads +""" + +[[exercises]] +name = "threads3" +dir = "20_threads" +hint = """ +An alternate way to handle concurrency between threads is to use an `mpsc` +(multiple producer, single consumer) channel to communicate. + +With both a sending end and a receiving end, it's possible to send values in +one thread and receive them in another. + +Multiple producers are possible by using clone() to create a duplicate of the +original sending end. + +See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. +""" + +# MACROS + +[[exercises]] +name = "macros1" +dir = "21_macros" +test = false +hint = """ +When you call a macro, you need to add something special compared to a +regular function call. If you're stuck, take a look at what's inside +`my_macro`.""" + +[[exercises]] +name = "macros2" +dir = "21_macros" +test = false +hint = """ +Macros don't quite play by the same rules as the rest of Rust, in terms of +what's available where. + +Unlike other things in Rust, the order of "where you define a macro" versus +"where you use it" actually matters.""" + +[[exercises]] +name = "macros3" +dir = "21_macros" +test = false +hint = """ +In order to use a macro outside of its module, you need to do something +special to the module to lift the macro out into its parent. + +The same trick also works on "extern crate" statements for crates that have +exported macros, if you've seen any of those around.""" + +[[exercises]] +name = "macros4" +dir = "21_macros" +test = false +hint = """ +You only need to add a single character to make this compile. + +The way macros are written, it wants to see something between each "macro arm", +so it can separate them. + +That's all the macro exercises we have in here, but it's barely even scratching +the surface of what you can do with Rust's macros. For a more thorough +introduction, you can have a read through 'The Little Book of Rust Macros': +https://veykril.github.io/tlborm/""" + +# CLIPPY + +[[exercises]] +name = "clippy1" +dir = "22_clippy" +test = false +strict_clippy = true +hint = """ +Rust stores the highest precision version of any long or infinite precision +mathematical constants in the Rust standard library: +https://doc.rust-lang.org/stable/std/f32/consts/index.html + +We may be tempted to use our own approximations for certain mathematical +constants, but clippy recognizes those imprecise mathematical constants as a +source of potential error. + +See the suggestions of the clippy warning in compile output and use the +appropriate replacement constant from `std::f32::consts`...""" + +[[exercises]] +name = "clippy2" +dir = "22_clippy" +test = false +strict_clippy = true +hint = """ +`for` loops over `Option` values are more clearly expressed as an `if let`""" + +[[exercises]] +name = "clippy3" +dir = "22_clippy" +test = false +strict_clippy = true +hint = "No hints this time!" + +# TYPE CONVERSIONS + +[[exercises]] +name = "using_as" +dir = "23_conversions" +hint = """ +Use the `as` operator to cast one of the operands in the last line of the +`average` function into the expected return type.""" + +[[exercises]] +name = "from_into" +dir = "23_conversions" +hint = """ +Follow the steps provided right before the `From` implementation""" + +[[exercises]] +name = "from_str" +dir = "23_conversions" +hint = """ +The implementation of `FromStr` should return an `Ok` with a `Person` object, +or an `Err` with an error if the string is not valid. + +This is almost like the `from_into` exercise, but returning errors instead +of falling back to a default value. + +Look at the test cases to see which error variants to return. + +Another hint: You can use the `map_err` method of `Result` with a function +or a closure to wrap the error from `parse::`. + +Yet another hint: If you would like to propagate errors by using the `?` +operator in your solution, you might want to look at +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html +""" + +[[exercises]] +name = "try_from_into" +dir = "23_conversions" +hint = """ +Follow the steps provided right before the `TryFrom` implementation. +You can also use the example at +https://doc.rust-lang.org/std/convert/trait.TryFrom.html + +Is there an implementation of `TryFrom` in the standard library that +can both do the required integer conversion and check the range of the input? + +Another hint: Look at the test cases to see which error variants to return. + +Yet another hint: You can use the `map_err` or `or` methods of `Result` to +convert errors. + +Yet another hint: If you would like to propagate errors by using the `?` +operator in your solution, you might want to look at +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html + +Challenge: Can you make the `TryFrom` implementations generic over many integer types?""" + +[[exercises]] +name = "as_ref_mut" +dir = "23_conversions" +hint = """ +Add `AsRef` or `AsMut` as a trait bound to the functions.""" diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index 4417a4f..6c6067b 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -15,7 +15,8 @@ struct InfoFile { #[proc_macro] pub fn include_files(_: TokenStream) -> TokenStream { - let exercises = toml_edit::de::from_str::(include_str!("../info.toml")) + let info_file = include_str!("../info.toml"); + let exercises = toml_edit::de::from_str::(info_file) .expect("Failed to parse `info.toml`") .exercises; @@ -46,6 +47,7 @@ pub fn include_files(_: TokenStream) -> TokenStream { quote! { EmbeddedFiles { + info_file: #info_file, 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) }),*] } diff --git a/src/embedded.rs b/src/embedded.rs index 23c8d6e..45f8eca 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -70,6 +70,7 @@ impl ExerciseDir { } pub struct EmbeddedFiles { + pub info_file: &'static str, exercise_files: &'static [ExerciseFiles], exercise_dirs: &'static [ExerciseDir], } @@ -148,7 +149,7 @@ mod tests { #[test] fn dirs() { - let exercises = toml_edit::de::from_str::(include_str!("../info.toml")) + let exercises = toml_edit::de::from_str::(EMBEDDED_FILES.info_file) .expect("Failed to parse `info.toml`") .exercises; diff --git a/src/info_file.rs b/src/info_file.rs index dbe4f08..14b886b 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -2,6 +2,8 @@ use anyhow::{bail, Context, Error, Result}; use serde::Deserialize; use std::{fs, io::ErrorKind}; +use crate::embedded::EMBEDDED_FILES; + // Deserialized from the `info.toml` file. #[derive(Deserialize)] pub struct ExerciseInfo { @@ -47,7 +49,7 @@ impl InfoFile { .context("Failed to parse the `info.toml` file")?, Err(e) => { if e.kind() == ErrorKind::NotFound { - return toml_edit::de::from_str(include_str!("../info.toml")) + return toml_edit::de::from_str(EMBEDDED_FILES.info_file) .context("Failed to parse the embedded `info.toml` file"); } -- cgit v1.2.3 From d2b5906be226f936481ff3a5cb8fccde5c721524 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 02:37:32 +0200 Subject: No more word input --- README.md | 4 ++-- exercises/00_intro/intro1.rs | 2 +- rustlings-macros/info.toml | 4 ++-- src/main.rs | 2 +- src/watch.rs | 2 +- src/watch/state.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) (limited to 'rustlings-macros') diff --git a/README.md b/README.md index 3ba080f..0180608 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ It will rerun the current exercise automatically every time you change the exerc

If detecting file changes in the exercises/ directory fails… (click to expand) -> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` (or `run`) in the watch mode. +> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode. > > Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). @@ -106,7 +106,7 @@ It will rerun the current exercise automatically every time you change the exerc ### Exercise List -In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` (or `list`) to open the interactive exercise list. +In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` to open the interactive exercise list. The list allows you to… diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 62bf95f..bdbf34b 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,6 +1,6 @@ // We sometimes encourage you to keep trying things on a given exercise, even // after you already figured it out. If you got everything working and feel -// ready for the next exercise, enter `n` (or `next`) in the terminal. +// ready for the next exercise, enter `n` in the terminal. // // The exercise file will be reloaded when you change one of the lines below! // Try adding a new `println!`. diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 4204f27..485665e 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -13,7 +13,7 @@ get started, here are some notes about how Rustlings operates: the exercise file in your editor, fix errors and save the file. Rustlings will automatically detect the file change and rerun the exercise. If all errors are fixed, Rustlings will ask you to move on to the next exercise. -3. If you're stuck on an exercise, enter `h` (or `hint`) to show a hint. +3. If you're stuck on an exercise, enter `h` to show a hint. 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, other learners do too so you can help each other out! @@ -35,7 +35,7 @@ name = "intro1" dir = "00_intro" test = false # TODO: Fix hint -hint = """Enter `n` (or `next`) followed by ENTER to move on to the next exercise""" +hint = """Enter `n` to move on to the next exercise. You might need to press ENTER after typing `n`.""" [[exercises]] name = "intro2" diff --git a/src/main.rs b/src/main.rs index 15bcc8e..cf6f0d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,7 @@ fn press_enter_prompt() -> io::Result<()> { struct Args { #[command(subcommand)] command: Option, - /// Manually run the current exercise using `r` or `run` in the watch mode. + /// Manually run the current exercise using `r` in the watch mode. /// Only use this if Rustlings fails to detect exercise file changes. #[arg(long)] manual_run: bool, diff --git a/src/watch.rs b/src/watch.rs index 7d4f54b..f72ebf7 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -123,5 +123,5 @@ The automatic detection of exercise file changes failed :( Please try running `rustlings` again. If you keep getting this error, run `rustlings --manual-run` to deactivate the file watcher. -You need to manually trigger running the current exercise using `r` (or `run`) then. +You need to manually trigger running the current exercise using `r` then. "; diff --git a/src/watch/state.rs b/src/watch/state.rs index 2e98546..c21d7ca 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -127,7 +127,7 @@ impl<'a> WatchState<'a> { self.writer, "{}\n", "Exercise done ✓ -When you are done experimenting, enter `n` (or `next`) to move on to the next exercise 🦀" +When you are done experimenting, enter `n` to move on to the next exercise 🦀" .bold() .green(), )?; -- cgit v1.2.3 From 0f4c42d54ea7322a4ee0ae7036c058c3061e80e9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 21 May 2024 01:47:57 +0200 Subject: Add solutions to intro and variables --- exercises/00_intro/intro2.rs | 4 ++-- exercises/01_variables/variables1.rs | 6 +++--- exercises/01_variables/variables2.rs | 2 ++ exercises/01_variables/variables3.rs | 4 +++- exercises/01_variables/variables4.rs | 9 ++++++--- exercises/01_variables/variables5.rs | 10 ++++++---- exercises/01_variables/variables6.rs | 4 +++- rustlings-macros/info.toml | 37 ++++++++++++++++++------------------ solutions/00_intro/intro1.rs | 3 ++- solutions/00_intro/intro2.rs | 5 ++++- solutions/01_variables/variables1.rs | 7 ++++++- solutions/01_variables/variables2.rs | 17 ++++++++++++++++- solutions/01_variables/variables3.rs | 14 +++++++++++++- solutions/01_variables/variables4.rs | 10 +++++++++- solutions/01_variables/variables5.rs | 10 +++++++++- solutions/01_variables/variables6.rs | 7 ++++++- 16 files changed, 109 insertions(+), 40 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs index c7a3ab2..e443ec8 100644 --- a/exercises/00_intro/intro2.rs +++ b/exercises/00_intro/intro2.rs @@ -1,5 +1,5 @@ -// Make the code print a greeting to the world. +// TODO: Fix the code to print "Hello world!". fn main() { - printline!("Hello there!") + printline!("Hello world!"); } diff --git a/exercises/01_variables/variables1.rs b/exercises/01_variables/variables1.rs index 3035bfa..0a9e554 100644 --- a/exercises/01_variables/variables1.rs +++ b/exercises/01_variables/variables1.rs @@ -1,6 +1,6 @@ -// Make me compile! - fn main() { + // TODO: Add missing keyword. x = 5; - println!("x has the value {}", x); + + println!("x has the value {x}"); } diff --git a/exercises/01_variables/variables2.rs b/exercises/01_variables/variables2.rs index ce2dd85..e2a3603 100644 --- a/exercises/01_variables/variables2.rs +++ b/exercises/01_variables/variables2.rs @@ -1,5 +1,7 @@ fn main() { + // TODO: Change the line below to fix the compiler error. let x; + if x == 10 { println!("x is ten!"); } else { diff --git a/exercises/01_variables/variables3.rs b/exercises/01_variables/variables3.rs index 488385b..06f35bb 100644 --- a/exercises/01_variables/variables3.rs +++ b/exercises/01_variables/variables3.rs @@ -1,4 +1,6 @@ fn main() { + // TODO: Change the line below to fix the compiler error. let x: i32; - println!("Number {}", x); + + println!("Number {x}"); } diff --git a/exercises/01_variables/variables4.rs b/exercises/01_variables/variables4.rs index 67be127..8634ceb 100644 --- a/exercises/01_variables/variables4.rs +++ b/exercises/01_variables/variables4.rs @@ -1,6 +1,9 @@ +// TODO: Fix the compiler error. + fn main() { let x = 3; - println!("Number {}", x); - x = 5; // don't change this line - println!("Number {}", x); + println!("Number {x}"); + + x = 5; // Don't change this line + println!("Number {x}"); } diff --git a/exercises/01_variables/variables5.rs b/exercises/01_variables/variables5.rs index 3a74541..73f655e 100644 --- a/exercises/01_variables/variables5.rs +++ b/exercises/01_variables/variables5.rs @@ -1,6 +1,8 @@ fn main() { - let number = "T-H-R-E-E"; // don't change this line - println!("Spell a Number : {}", number); - number = 3; // don't rename this variable - println!("Number plus two is : {}", number + 2); + let number = "T-H-R-E-E"; // Don't change this line + println!("Spell a number: {}", number); + + // TODO: Fix the compiler error by changing the line below without renaming the the variable. + number = 3; + println!("Number plus two is: {}", number + 2); } diff --git a/exercises/01_variables/variables6.rs b/exercises/01_variables/variables6.rs index 4746331..4a040fd 100644 --- a/exercises/01_variables/variables6.rs +++ b/exercises/01_variables/variables6.rs @@ -1,4 +1,6 @@ +// TODO: Change the line below to fix the compiler error. const NUMBER = 3; + fn main() { - println!("Number {}", NUMBER); + println!("Number: {NUMBER}"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 485665e..be3b262 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -29,20 +29,21 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md # INTRO -# TODO: Update exercise [[exercises]] name = "intro1" dir = "00_intro" test = false -# TODO: Fix hint -hint = """Enter `n` to move on to the next exercise. You might need to press ENTER after typing `n`.""" +hint = """ +Enter `n` to move on to the next exercise. +You might need to press ENTER after typing `n`.""" [[exercises]] name = "intro2" dir = "00_intro" test = false hint = """ -The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" +The compiler is informing us that we've got the name of the print macro wrong. +It also suggests an alternative.""" # VARIABLES @@ -51,18 +52,18 @@ name = "variables1" dir = "01_variables" test = false hint = """ -The declaration in the first line in the main function is missing a keyword -that is needed in Rust to create a new variable binding.""" +The declaration in the `main` function is missing a keyword that is needed +in Rust to create a new variable binding.""" [[exercises]] name = "variables2" dir = "01_variables" test = false hint = """ -The compiler message is saying that Rust cannot infer the type that the +The compiler message is saying that Rust can't infer the type that the variable binding `x` has with what is given here. -What happens if you annotate the first line in the main function with a type +What happens if you annotate the first line in the `main` function with a type annotation? What if you give `x` a value? @@ -78,9 +79,9 @@ name = "variables3" dir = "01_variables" test = false hint = """ -Oops! In this exercise, we have a variable binding that we've created on in the -first line in the `main` function, and we're trying to use it in the next line, -but we haven't given it a value. +In this exercise, we have a variable binding that we've created in the `main` +function, and we're trying to use it in the next line, but we haven't given it +a value. We can't print out something that isn't there; try giving `x` a value! @@ -92,7 +93,7 @@ name = "variables4" dir = "01_variables" test = false hint = """ -In Rust, variable bindings are immutable by default. But here we're trying +In Rust, variable bindings are immutable by default. But here, we're trying to reassign a different value to `x`! There's a keyword we can use to make a variable binding mutable instead.""" @@ -120,12 +121,12 @@ dir = "01_variables" test = false hint = """ We know about variables and mutability, but there is another important type of -variable available: constants. +variables available: constants. -Constants are always immutable and they are declared with keyword `const` rather -than keyword `let`. +Constants are always immutable. They are declared with the keyword `const` instead +of `let`. -Constants types must also always be annotated. +The type of Constants must always be annotated. Read more about constants and the differences between variables and constants under 'Constants' in the book's section 'Variables and Mutability': @@ -139,7 +140,7 @@ name = "functions1" dir = "02_functions" test = false hint = """ -This main function is calling a function that it expects to exist, but the +This `main` function is calling a function that it expects to exist, but the function doesn't exist. It expects this function to have the name `call_me`. It expects this function to not take any arguments and not return a value. Sounds a lot like `main`, doesn't it?""" @@ -688,7 +689,7 @@ test = false hint = """ If other functions can return a `Result`, why shouldn't `main`? It's a fairly common convention to return something like `Result<(), ErrorType>` from your -main function. +`main` function. The unit (`()`) type is there because nothing is really needed in terms of positive results.""" diff --git a/solutions/00_intro/intro1.rs b/solutions/00_intro/intro1.rs index 4e18198..07d4e4f 100644 --- a/solutions/00_intro/intro1.rs +++ b/solutions/00_intro/intro1.rs @@ -1 +1,2 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// The exercise `intro1` only requires entering `n` in the terminal to go to the next exercise. +// It is just an introduction to how Rustlings works. diff --git a/solutions/00_intro/intro2.rs b/solutions/00_intro/intro2.rs index 4e18198..b8e031a 100644 --- a/solutions/00_intro/intro2.rs +++ b/solutions/00_intro/intro2.rs @@ -1 +1,4 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // `println!` instead of `printline!`. + println!("Hello world!"); +} diff --git a/solutions/01_variables/variables1.rs b/solutions/01_variables/variables1.rs index 4e18198..58d046b 100644 --- a/solutions/01_variables/variables1.rs +++ b/solutions/01_variables/variables1.rs @@ -1 +1,6 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // Declaring variables requires the `let` keyword. + let x = 5; + + println!("x has the value {x}"); +} diff --git a/solutions/01_variables/variables2.rs b/solutions/01_variables/variables2.rs index 4e18198..50b8d1b 100644 --- a/solutions/01_variables/variables2.rs +++ b/solutions/01_variables/variables2.rs @@ -1 +1,16 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // The easiest way to fix the compiler error is to initialize the + // variable `x`. By setting its value to an integer, Rust infers its type + // as `i32` which is the default type for integers. + let x = 42; + + // But we can enforce a type different from the default `i32` by adding + // a type annotation: + // let x: u8 = 42; + + if x == 10 { + println!("x is ten!"); + } else { + println!("x is not ten!"); + } +} diff --git a/solutions/01_variables/variables3.rs b/solutions/01_variables/variables3.rs index 4e18198..7db42a9 100644 --- a/solutions/01_variables/variables3.rs +++ b/solutions/01_variables/variables3.rs @@ -1 +1,13 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // Reading uninitialized variables isn't allowed in Rust! + // Therefore, we need to assign a value first. + let x: i32 = 42; + + println!("Number {x}"); + + // It possible to declare a variable and initialize it later. + // But it can't be used before initialization. + let y: i32; + y = 42; + println!("Number {y}"); +} diff --git a/solutions/01_variables/variables4.rs b/solutions/01_variables/variables4.rs index 4e18198..0540caa 100644 --- a/solutions/01_variables/variables4.rs +++ b/solutions/01_variables/variables4.rs @@ -1 +1,9 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // In Rust, variables are immutable by default. + // Adding the `mut` keyword after `let` makes the declared variable mutable. + let mut x = 3; + println!("Number {x}"); + + x = 5; // Don't change this line + println!("Number {x}"); +} diff --git a/solutions/01_variables/variables5.rs b/solutions/01_variables/variables5.rs index 4e18198..456dc9c 100644 --- a/solutions/01_variables/variables5.rs +++ b/solutions/01_variables/variables5.rs @@ -1 +1,9 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + let number = "T-H-R-E-E"; // Don't change this line + println!("Spell a number: {}", number); + + // Using variable shadowing + // https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing + let number = 3; + println!("Number plus two is: {}", number + 2); +} diff --git a/solutions/01_variables/variables6.rs b/solutions/01_variables/variables6.rs index 4e18198..25b7a1e 100644 --- a/solutions/01_variables/variables6.rs +++ b/solutions/01_variables/variables6.rs @@ -1 +1,6 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// The type of constants must always be annotated. +const NUMBER: u64 = 3; + +fn main() { + println!("Number: {NUMBER}"); +} -- cgit v1.2.3 From d0b843d6c4a99636d3dc6caf3ceebea14cb3b07d Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 21 May 2024 02:43:18 +0200 Subject: Add solutions to functions --- exercises/02_functions/functions1.rs | 4 +++- exercises/02_functions/functions2.rs | 9 +++++---- exercises/02_functions/functions3.rs | 9 +++++---- exercises/02_functions/functions4.rs | 19 ++++++++++--------- exercises/02_functions/functions5.rs | 11 ++++++----- rustlings-macros/info.toml | 22 +++++++++++----------- solutions/01_variables/variables4.rs | 2 +- solutions/01_variables/variables5.rs | 2 +- solutions/02_functions/functions1.rs | 9 ++++++++- solutions/02_functions/functions2.rs | 12 +++++++++++- solutions/02_functions/functions3.rs | 11 ++++++++++- solutions/02_functions/functions4.rs | 18 +++++++++++++++++- solutions/02_functions/functions5.rs | 10 +++++++++- 13 files changed, 97 insertions(+), 41 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/02_functions/functions1.rs b/exercises/02_functions/functions1.rs index 4e3b103..a812c21 100644 --- a/exercises/02_functions/functions1.rs +++ b/exercises/02_functions/functions1.rs @@ -1,3 +1,5 @@ +// TODO: Add some function with the name `call_me` without arguments or a return value. + fn main() { - call_me(); + call_me(); // Don't change this line } diff --git a/exercises/02_functions/functions2.rs b/exercises/02_functions/functions2.rs index 84e09cd..2c773c6 100644 --- a/exercises/02_functions/functions2.rs +++ b/exercises/02_functions/functions2.rs @@ -1,9 +1,10 @@ -fn main() { - call_me(3); -} - +// TODO: Add the missing type of the argument `num` after the colon `:`. fn call_me(num:) { for i in 0..num { println!("Ring! Call number {}", i + 1); } } + +fn main() { + call_me(3); +} diff --git a/exercises/02_functions/functions3.rs b/exercises/02_functions/functions3.rs index 66fb6d3..5d5122a 100644 --- a/exercises/02_functions/functions3.rs +++ b/exercises/02_functions/functions3.rs @@ -1,9 +1,10 @@ -fn main() { - call_me(); -} - fn call_me(num: u32) { for i in 0..num { println!("Ring! Call number {}", i + 1); } } + +fn main() { + // TODO: Fix the function call. + call_me(); +} diff --git a/exercises/02_functions/functions4.rs b/exercises/02_functions/functions4.rs index 06d3b1b..b22bffd 100644 --- a/exercises/02_functions/functions4.rs +++ b/exercises/02_functions/functions4.rs @@ -1,14 +1,14 @@ // This store is having a sale where if the price is an even number, you get 10 -// Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. (Don't worry -// about the function bodies themselves, we're only interested in the signatures -// for now. If anything, this is a good way to peek ahead to future exercises!) +// Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. +// Don't worry about the function bodies themselves, we are only interested in +// the signatures for now. -fn main() { - let original_price = 51; - println!("Your sale price is {}", sale_price(original_price)); +fn is_even(num: i64) -> bool { + num % 2 == 0 } -fn sale_price(price: i32) -> { +// TODO: Fix the function signature. +fn sale_price(price: i64) -> { if is_even(price) { price - 10 } else { @@ -16,6 +16,7 @@ fn sale_price(price: i32) -> { } } -fn is_even(num: i32) -> bool { - num % 2 == 0 +fn main() { + let original_price = 51; + println!("Your sale price is {}", sale_price(original_price)); } diff --git a/exercises/02_functions/functions5.rs b/exercises/02_functions/functions5.rs index 3bb5e52..31fd057 100644 --- a/exercises/02_functions/functions5.rs +++ b/exercises/02_functions/functions5.rs @@ -1,8 +1,9 @@ -fn main() { - let answer = square(3); - println!("The square of 3 is {}", answer); -} - +// TODO: Fix the function body without chaning the signature. fn square(num: i32) -> i32 { num * num; } + +fn main() { + let answer = square(3); + println!("The square of 3 is {answer}"); +} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index be3b262..495e9c3 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -142,7 +142,7 @@ test = false hint = """ This `main` function is calling a function that it expects to exist, but the function doesn't exist. It expects this function to have the name `call_me`. -It expects this function to not take any arguments and not return a value. +It also expects this function to not take any arguments and not return a value. Sounds a lot like `main`, doesn't it?""" [[exercises]] @@ -159,7 +159,7 @@ dir = "02_functions" test = false hint = """ This time, the function *declaration* is okay, but there's something wrong -with the place where we're calling the function.""" +with the place where we are calling the function.""" [[exercises]] name = "functions4" @@ -167,8 +167,8 @@ dir = "02_functions" test = false hint = """ The error message points to the function `sale_price` and says it expects a type -after the `->`. This is where the function's return type should be -- take a -look at the `is_even` function for an example!""" +after `->`. This is where the function's return type should be. +Take a look at the `is_even` function for an example!""" [[exercises]] name = "functions5" @@ -177,15 +177,15 @@ test = false hint = """ This is a really common error that can be fixed by removing one character. It happens because Rust distinguishes between expressions and statements: -expressions return a value based on their operand(s), and statements simply -return a `()` type which behaves just like `void` in C/C++ language. +Expressions return a value based on their operand(s), and statements simply +return a `()` type which behaves just like `void` in C/C++. -We want to return a value of `i32` type from the `square` function, but it is -returning a `()` type... +We want to return a value with the type `i32` from the `square` function, but +it is returning the type `()`. -They are not the same. There are two solutions: -1. Add a `return` ahead of `num * num;` -2. remove `;`, make it to be `num * num`""" +There are two solutions: +1. Add the `return` keyword before `num * num;` +2. Remove the semicolon `;` after `num * num`""" # IF diff --git a/solutions/01_variables/variables4.rs b/solutions/01_variables/variables4.rs index 0540caa..7de6bcb 100644 --- a/solutions/01_variables/variables4.rs +++ b/solutions/01_variables/variables4.rs @@ -4,6 +4,6 @@ fn main() { let mut x = 3; println!("Number {x}"); - x = 5; // Don't change this line + x = 5; println!("Number {x}"); } diff --git a/solutions/01_variables/variables5.rs b/solutions/01_variables/variables5.rs index 456dc9c..9057754 100644 --- a/solutions/01_variables/variables5.rs +++ b/solutions/01_variables/variables5.rs @@ -1,5 +1,5 @@ fn main() { - let number = "T-H-R-E-E"; // Don't change this line + let number = "T-H-R-E-E"; println!("Spell a number: {}", number); // Using variable shadowing diff --git a/solutions/02_functions/functions1.rs b/solutions/02_functions/functions1.rs index 4e18198..dc52744 100644 --- a/solutions/02_functions/functions1.rs +++ b/solutions/02_functions/functions1.rs @@ -1 +1,8 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// Some function with the name `call_me` without arguments or a return value. +fn call_me() { + println!("Hello world!"); +} + +fn main() { + call_me(); +} diff --git a/solutions/02_functions/functions2.rs b/solutions/02_functions/functions2.rs index 4e18198..f14ffa3 100644 --- a/solutions/02_functions/functions2.rs +++ b/solutions/02_functions/functions2.rs @@ -1 +1,11 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// The type of function arguments must be annotated. +// Added the type annotation `u64`. +fn call_me(num: u64) { + for i in 0..num { + println!("Ring! Call number {}", i + 1); + } +} + +fn main() { + call_me(3); +} diff --git a/solutions/02_functions/functions3.rs b/solutions/02_functions/functions3.rs index 4e18198..c581c42 100644 --- a/solutions/02_functions/functions3.rs +++ b/solutions/02_functions/functions3.rs @@ -1 +1,10 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn call_me(num: u32) { + for i in 0..num { + println!("Ring! Call number {}", i + 1); + } +} + +fn main() { + // `call_me` expects an argument. + call_me(5); +} diff --git a/solutions/02_functions/functions4.rs b/solutions/02_functions/functions4.rs index 4e18198..f823de2 100644 --- a/solutions/02_functions/functions4.rs +++ b/solutions/02_functions/functions4.rs @@ -1 +1,17 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn is_even(num: i64) -> bool { + num % 2 == 0 +} + +// The return type must always be annotated. +fn sale_price(price: i64) -> i64 { + if is_even(price) { + price - 10 + } else { + price - 3 + } +} + +fn main() { + let original_price = 51; + println!("Your sale price is {}", sale_price(original_price)); +} diff --git a/solutions/02_functions/functions5.rs b/solutions/02_functions/functions5.rs index 4e18198..0335418 100644 --- a/solutions/02_functions/functions5.rs +++ b/solutions/02_functions/functions5.rs @@ -1 +1,9 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn square(num: i32) -> i32 { + // Removed the semicolon `;` at the end of the line below to implicitely return the result. + num * num +} + +fn main() { + let answer = square(3); + println!("The square of 3 is {answer}"); +} -- cgit v1.2.3 From c8ad6c3960b4bec44a610cc144e6b635bffcbc31 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 22 May 2024 15:04:21 +0200 Subject: if1 solution --- rustlings-macros/info.toml | 4 ++-- solutions/03_if/if1.rs | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) (limited to 'rustlings-macros') diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 495e9c3..a67e38d 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -200,9 +200,9 @@ Some similar examples from other languages: - In Python this would be: `a if a > b else b` Remember in Rust that: -- the `if` condition does not need to be surrounded by parentheses +- The `if` condition does not need to be surrounded by parentheses - `if`/`else` conditionals are expressions -- Each condition is followed by a `{}` block.""" +- Each condition is followed by a `{}` block""" [[exercises]] name = "if2" diff --git a/solutions/03_if/if1.rs b/solutions/03_if/if1.rs index 4e18198..079c671 100644 --- a/solutions/03_if/if1.rs +++ b/solutions/03_if/if1.rs @@ -1 +1,32 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn bigger(a: i32, b: i32) -> i32 { + if a > b { + a + } else { + b + } +} + +fn main() { + // You can optionally experiment here. +} + +// Don't mind this for now :) +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ten_is_bigger_than_eight() { + assert_eq!(10, bigger(10, 8)); + } + + #[test] + fn fortytwo_is_bigger_than_thirtytwo() { + assert_eq!(42, bigger(32, 42)); + } + + #[test] + fn equal_numbers() { + assert_eq!(42, bigger(42, 42)); + } +} -- cgit v1.2.3 From eafb157d60ee46e95e2b54ec75b33180a838b0c5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 22 May 2024 15:16:50 +0200 Subject: if2 solution --- exercises/03_if/if2.rs | 8 ++++---- rustlings-macros/info.toml | 6 ++++-- solutions/03_if/if2.rs | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 7 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index 1b65596..593a77a 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -1,6 +1,4 @@ -// Step 1: Make me compile! -// Step 2: Get the bar_for_fuzz and default_to_baz tests passing! - +// TODO: Fix the compiler error on this function. fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" @@ -13,13 +11,15 @@ fn main() { // You can optionally experiment here. } -// No test changes needed! +// TODO: Read the tests to understand the desired behavior. +// Make all tests pass without changing them. #[cfg(test)] mod tests { use super::*; #[test] fn foo_for_fizz() { + // This means that calling `foo_if_fizz` with the argument "fizz" should return "foo". assert_eq!(foo_if_fizz("fizz"), "foo"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index a67e38d..39fc5c4 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -209,8 +209,10 @@ name = "if2" dir = "03_if" hint = """ For that first compiler error, it's important in Rust that each conditional -block returns the same type! To get the tests passing, you will need a couple -conditions checking different input values.""" +block returns the same type! + +To get the tests passing, you will need a couple conditions checking different +input values. Read the tests to find out what they expect.""" [[exercises]] name = "if3" diff --git a/solutions/03_if/if2.rs b/solutions/03_if/if2.rs index 4e18198..440bba0 100644 --- a/solutions/03_if/if2.rs +++ b/solutions/03_if/if2.rs @@ -1 +1,33 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn foo_if_fizz(fizzish: &str) -> &str { + if fizzish == "fizz" { + "foo" + } else if fizzish == "fuzz" { + "bar" + } else { + "baz" + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn foo_for_fizz() { + assert_eq!(foo_if_fizz("fizz"), "foo"); + } + + #[test] + fn bar_for_fuzz() { + assert_eq!(foo_if_fizz("fuzz"), "bar"); + } + + #[test] + fn default_to_baz() { + assert_eq!(foo_if_fizz("literally anything"), "baz"); + } +} -- cgit v1.2.3 From 990c68efcba8e1e2b7f2d8c5b6c16885d3920010 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 25 May 2024 16:31:21 +0200 Subject: primitive_types1 solution --- exercises/04_primitive_types/primitive_types1.rs | 8 +++----- rustlings-macros/info.toml | 5 ++++- solutions/04_primitive_types/primitive_types1.rs | 12 +++++++++++- 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/04_primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs index 0002651..750d6e5 100644 --- a/exercises/04_primitive_types/primitive_types1.rs +++ b/exercises/04_primitive_types/primitive_types1.rs @@ -1,14 +1,12 @@ -// Fill in the rest of the line that has code missing! - fn main() { - // Booleans (`bool`) - let is_morning = true; if is_morning { println!("Good morning!"); } - let // Finish the rest of this line like the example! Or make it be false! + // TODO: Define a boolean variable with the name `is_evening` before the `if` statement below. + // The value of the variable should be the negation (opposite) of `is_morning`. + // let … if is_evening { println!("Good evening!"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 39fc5c4..59de7f2 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -234,7 +234,10 @@ hint = "No hints this time ;)" name = "primitive_types1" dir = "04_primitive_types" test = false -hint = "No hints this time ;)" +hint = """ +In Rust, a boolean can be negated using the operator `!` before it. +Example: `!true == false` +This also works with boolean variables.""" [[exercises]] name = "primitive_types2" diff --git a/solutions/04_primitive_types/primitive_types1.rs b/solutions/04_primitive_types/primitive_types1.rs index 4e18198..fac6ec0 100644 --- a/solutions/04_primitive_types/primitive_types1.rs +++ b/solutions/04_primitive_types/primitive_types1.rs @@ -1 +1,11 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + let is_morning = true; + if is_morning { + println!("Good morning!"); + } + + let is_evening = !is_morning; + if is_evening { + println!("Good evening!"); + } +} -- cgit v1.2.3 From 0338b1cbdfa567d5f9580afef1d4483c7d275c32 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 8 Jun 2024 21:43:38 +0200 Subject: primitive_types3 solution --- exercises/04_primitive_types/primitive_types3.rs | 7 +++---- rustlings-macros/info.toml | 2 +- solutions/04_primitive_types/primitive_types3.rs | 12 +++++++++++- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/04_primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs index 5095fc4..bef5579 100644 --- a/exercises/04_primitive_types/primitive_types3.rs +++ b/exercises/04_primitive_types/primitive_types3.rs @@ -1,12 +1,11 @@ -// Create an array with at least 100 elements in it where the ??? is. - fn main() { - let a = ??? + // TODO: Create an array with at least 100 elements in it where the ??? is. + // let a = ??? if a.len() >= 100 { println!("Wow, that's a big array!"); } else { println!("Meh, I eat arrays like that for breakfast."); - panic!("Array not big enough, more elements needed") + panic!("Array not big enough, more elements needed"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 59de7f2..fc0bee8 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -250,7 +250,7 @@ name = "primitive_types3" dir = "04_primitive_types" test = false hint = """ -There's a shorthand to initialize Arrays with a certain size that does not +There's a shorthand to initialize arrays with a certain size that doesn't require you to type in 100 items (but you certainly can if you want!). For example, you can do: diff --git a/solutions/04_primitive_types/primitive_types3.rs b/solutions/04_primitive_types/primitive_types3.rs index 4e18198..8dd109f 100644 --- a/solutions/04_primitive_types/primitive_types3.rs +++ b/solutions/04_primitive_types/primitive_types3.rs @@ -1 +1,11 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // An array with 100 elements of the value 42. + let a = [42; 100]; + + if a.len() >= 100 { + println!("Wow, that's a big array!"); + } else { + println!("Meh, I eat arrays like that for breakfast."); + panic!("Array not big enough, more elements needed"); + } +} -- cgit v1.2.3 From 98db5790144a0d32009718e54051b3f7d54ae494 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 8 Jun 2024 23:42:15 +0200 Subject: primitive_types4 solution --- exercises/04_primitive_types/primitive_types3.rs | 2 +- exercises/04_primitive_types/primitive_types4.rs | 7 ++----- rustlings-macros/info.toml | 4 ++-- solutions/04_primitive_types/primitive_types4.rs | 24 +++++++++++++++++++++++- 4 files changed, 28 insertions(+), 9 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/04_primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs index bef5579..9b79c0c 100644 --- a/exercises/04_primitive_types/primitive_types3.rs +++ b/exercises/04_primitive_types/primitive_types3.rs @@ -1,5 +1,5 @@ fn main() { - // TODO: Create an array with at least 100 elements in it where the ??? is. + // TODO: Create an array called `a` with at least 100 elements in it. // let a = ??? if a.len() >= 100 { diff --git a/exercises/04_primitive_types/primitive_types4.rs b/exercises/04_primitive_types/primitive_types4.rs index 661e051..16e4fd9 100644 --- a/exercises/04_primitive_types/primitive_types4.rs +++ b/exercises/04_primitive_types/primitive_types4.rs @@ -1,18 +1,15 @@ -// Get a slice out of Array a where the ??? is so that the test passes. - fn main() { // You can optionally experiment here. } #[cfg(test)] mod tests { - use super::*; - #[test] fn slice_out_of_array() { let a = [1, 2, 3, 4, 5]; - let nice_slice = ??? + // TODO: Get a slice called `nice_slice` out of the array `a` so that the test passes. + // let nice_slice = ??? assert_eq!([2, 3, 4], nice_slice); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index fc0bee8..313ba6f 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -267,8 +267,8 @@ dir = "04_primitive_types" hint = """ Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section of the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the -starting and ending (plus one) indices of the items in the `Array` that you -want to end up in the slice. +starting and ending (plus one) indices of the items in the array that you want +to end up in the slice. If you're curious why the first argument of `assert_eq!` does not have an ampersand for a reference since the second argument is a reference, take a look diff --git a/solutions/04_primitive_types/primitive_types4.rs b/solutions/04_primitive_types/primitive_types4.rs index 4e18198..4807e66 100644 --- a/solutions/04_primitive_types/primitive_types4.rs +++ b/solutions/04_primitive_types/primitive_types4.rs @@ -1 +1,23 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn slice_out_of_array() { + let a = [1, 2, 3, 4, 5]; + // 0 1 2 3 4 <- indices + // ------- + // | + // +--- slice + + // Note that the upper index 4 is excluded. + let nice_slice = &a[1..4]; + assert_eq!([2, 3, 4], nice_slice); + + // The upper index can be included by using the syntax `..=` (with `=` sign) + let nice_slice = &a[1..=3]; + assert_eq!([2, 3, 4], nice_slice); + } +} -- cgit v1.2.3 From 532c9ebb30afa226590e68e87af11da42b598974 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 19 Jun 2024 14:17:06 +0200 Subject: primitive_types5 solution --- exercises/04_primitive_types/primitive_types5.rs | 8 ++++---- rustlings-macros/info.toml | 2 +- solutions/04_primitive_types/primitive_types5.rs | 9 ++++++++- 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/04_primitive_types/primitive_types5.rs b/exercises/04_primitive_types/primitive_types5.rs index f2216a5..6e00ef5 100644 --- a/exercises/04_primitive_types/primitive_types5.rs +++ b/exercises/04_primitive_types/primitive_types5.rs @@ -1,8 +1,8 @@ -// Destructure the `cat` tuple so that the println will work. - fn main() { let cat = ("Furry McFurson", 3.5); - let /* your pattern here */ = cat; - println!("{} is {} years old.", name, age); + // TODO: Destructure the `cat` tuple in one statement so that the println works. + // let /* your pattern here */ = cat; + + println!("{name} is {age} years old"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 313ba6f..e5f5877 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -286,7 +286,7 @@ Particularly the part about destructuring (second to last example in the section). You'll need to make a pattern to bind `name` and `age` to the appropriate parts -of the tuple. You can do it!!""" +of the tuple.""" [[exercises]] name = "primitive_types6" diff --git a/solutions/04_primitive_types/primitive_types5.rs b/solutions/04_primitive_types/primitive_types5.rs index 4e18198..46d7ae8 100644 --- a/solutions/04_primitive_types/primitive_types5.rs +++ b/solutions/04_primitive_types/primitive_types5.rs @@ -1 +1,8 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + let cat = ("Furry McFurson", 3.5); + + // Destructuring the tuple. + let (name, age) = cat; + + println!("{name} is {age} years old"); +} -- cgit v1.2.3 From 0abcdeed42957ca805a3a7475fb3f14085af346e Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 19 Jun 2024 14:25:29 +0200 Subject: primitive_types6 solution --- exercises/04_primitive_types/primitive_types6.rs | 14 +++++--------- rustlings-macros/info.toml | 2 +- solutions/04_primitive_types/primitive_types6.rs | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 11 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/04_primitive_types/primitive_types6.rs b/exercises/04_primitive_types/primitive_types6.rs index 83cec24..a97e531 100644 --- a/exercises/04_primitive_types/primitive_types6.rs +++ b/exercises/04_primitive_types/primitive_types6.rs @@ -1,21 +1,17 @@ -// Use a tuple index to access the second element of `numbers`. You can put the -// expression for the second element where ??? is so that the test passes. - fn main() { // You can optionally experiment here. } #[cfg(test)] mod tests { - use super::*; - #[test] fn indexing_tuple() { let numbers = (1, 2, 3); - // Replace below ??? with the tuple indexing syntax. - let second = ???; - assert_eq!(2, second, - "This is not the 2nd number in the tuple!") + // TODO: Use a tuple index to access the second element of `numbers` + // and assign it to a variable called `second`. + // let second = ???; + + assert_eq!(second, 2, "This is not the 2nd number in the tuple!"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index e5f5877..cd85dcc 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -296,7 +296,7 @@ While you could use a destructuring `let` for the tuple here, try indexing into it instead, as explained in the last example of the 'Data Types -> The Tuple Type' section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type -Now you have another tool in your toolbox!""" +Now, you have another tool in your toolbox!""" # VECS diff --git a/solutions/04_primitive_types/primitive_types6.rs b/solutions/04_primitive_types/primitive_types6.rs index 4e18198..9b7c277 100644 --- a/solutions/04_primitive_types/primitive_types6.rs +++ b/solutions/04_primitive_types/primitive_types6.rs @@ -1 +1,16 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn indexing_tuple() { + let numbers = (1, 2, 3); + + // Tuple indexing syntax. + let second = numbers.1; + + assert_eq!(second, 2, "This is not the 2nd number in the tuple!"); + } +} -- cgit v1.2.3 From a9f0c7bf1f00ab19733953d3121d462eede34466 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 20 Jun 2024 01:00:06 +0200 Subject: vecs1 solution --- exercises/05_vecs/vecs1.rs | 14 ++++++-------- rustlings-macros/info.toml | 5 +++-- solutions/05_vecs/vecs1.rs | 24 +++++++++++++++++++++++- 3 files changed, 32 insertions(+), 11 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/05_vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs index ddcad84..68e1aff 100644 --- a/exercises/05_vecs/vecs1.rs +++ b/exercises/05_vecs/vecs1.rs @@ -1,11 +1,9 @@ -// Your task is to create a `Vec` which holds the exact same elements as in the -// array `a`. -// -// Make me compile and pass the test! - fn array_and_vec() -> ([i32; 4], Vec) { - let a = [10, 20, 30, 40]; // a plain array - let v = // TODO: declare your vector here with the macro for vectors + let a = [10, 20, 30, 40]; // Array + + // TODO: Create a vector called `v` which contains the exact same elements as in the array `a`. + // Use the vector macro. + // let v = ???; (a, v) } @@ -21,6 +19,6 @@ mod tests { #[test] fn test_array_and_vec_similarity() { let (a, v) = array_and_vec(); - assert_eq!(a, v[..]); + assert_eq!(a, *v); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index cd85dcc..21a27dd 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -307,8 +307,9 @@ hint = """ In Rust, there are two ways to define a Vector. 1. One way is to use the `Vec::new()` function to create a new vector and fill it with the `push()` method. -2. The second way, which is simpler is to use the `vec![]` macro and - define your elements inside the square brackets. +2. The second way is to use the `vec![]` macro and define your elements + inside the square brackets. This way is simpler when you exactly know + the initial values. Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html of the Rust book to learn more. diff --git a/solutions/05_vecs/vecs1.rs b/solutions/05_vecs/vecs1.rs index 4e18198..55b5676 100644 --- a/solutions/05_vecs/vecs1.rs +++ b/solutions/05_vecs/vecs1.rs @@ -1 +1,23 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn array_and_vec() -> ([i32; 4], Vec) { + let a = [10, 20, 30, 40]; // Array + + // Used the `vec!` macro. + let v = vec![10, 20, 30, 40]; + + (a, v) +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_array_and_vec_similarity() { + let (a, v) = array_and_vec(); + assert_eq!(a, *v); + } +} -- cgit v1.2.3 From 835ec7262247a341295c1d6f3772901a5fad5148 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 14:52:11 +0200 Subject: vecs2 solution + significant change to have a better comparison between both methods --- exercises/05_vecs/vecs2.rs | 62 +++++++++++++++++++++++++++------------------- rustlings-macros/info.toml | 11 +++----- solutions/05_vecs/vecs2.rs | 51 +++++++++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 34 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs index e72209c..a9be258 100644 --- a/exercises/05_vecs/vecs2.rs +++ b/exercises/05_vecs/vecs2.rs @@ -1,25 +1,32 @@ -// A Vec of even numbers is given. Your task is to complete the loop so that -// each number in the Vec is multiplied by 2. -// -// Make me pass the test! - -fn vec_loop(mut v: Vec) -> Vec { - for element in v.iter_mut() { - // TODO: Fill this up so that each element in the Vec `v` is - // multiplied by 2. - ??? +fn vec_loop(input: &[i32]) -> Vec { + let mut output = Vec::new(); + + for element in input { + // TODO: Multiply each element in the `input` slice by 2 and push it to + // the `output` vector. } - // At this point, `v` should be equal to [4, 8, 12, 16, 20]. - v + output +} + +fn vec_map_example(input: &[i32]) -> Vec { + // An example of collecting a vector after mapping. + // We map each element of the `input` slice to its value plus 1. + // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`. + input.iter().map(|element| element + 1).collect() } -fn vec_map(v: &Vec) -> Vec { - v.iter().map(|element| { - // TODO: Do the same thing as above - but instead of mutating the - // Vec, you can just return the new number! - ??? - }).collect() +fn vec_map(input: &[i32]) -> Vec { + // TODO: Here, we also want to multiply each element in the `input` slice + // by 2, but with iterator mapping instead of manually pushing into an empty + // vector. + // See the example in the function `vec_map_example` above. + input + .iter() + .map(|element| { + // ??? + }) + .collect() } fn main() { @@ -32,17 +39,22 @@ mod tests { #[test] fn test_vec_loop() { - let v: Vec = (1..).filter(|x| x % 2 == 0).take(5).collect(); - let ans = vec_loop(v.clone()); + let input = [2, 4, 6, 8, 10]; + let ans = vec_loop(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); + } - assert_eq!(ans, v.iter().map(|x| x * 2).collect::>()); + #[test] + fn test_vec_map_example() { + let input = [1, 2, 3]; + let ans = vec_map_example(&input); + assert_eq!(ans, [2, 3, 4]); } #[test] fn test_vec_map() { - let v: Vec = (1..).filter(|x| x % 2 == 0).take(5).collect(); - let ans = vec_map(&v); - - assert_eq!(ans, v.iter().map(|x| x * 2).collect::>()); + let input = [2, 4, 6, 8, 10]; + let ans = vec_map(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 21a27dd..3edd1c6 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -319,15 +319,10 @@ of the Rust book to learn more. name = "vecs2" dir = "05_vecs" hint = """ -In the first function we are looping over the Vector and getting a reference to -one `element` at a time. +In the first function, we create an empty vector and want to push new elements +to it. -To modify the value of that `element` we need to use the `*` dereference -operator. You can learn more in this chapter of the Rust book: -https://doc.rust-lang.org/stable/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector - -In the second function this dereferencing is not necessary, because the `map` -function expects the new value to be returned. +In the second function, we map the values of the input and collect them into a vector. After you've completed both functions, decide for yourself which approach you like better. diff --git a/solutions/05_vecs/vecs2.rs b/solutions/05_vecs/vecs2.rs index 4e18198..32c1c0f 100644 --- a/solutions/05_vecs/vecs2.rs +++ b/solutions/05_vecs/vecs2.rs @@ -1 +1,50 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn vec_loop(input: &[i32]) -> Vec { + let mut output = Vec::new(); + + for element in input { + output.push(2 * element); + } + + output +} + +fn vec_map_example(input: &[i32]) -> Vec { + // An example of collecting a vector after mapping. + // We map each element of the `input` slice to its value plus 1. + // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`. + input.iter().map(|element| element + 1).collect() +} + +fn vec_map(input: &[i32]) -> Vec { + input.iter().map(|element| 2 * element).collect() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_vec_loop() { + let input = [2, 4, 6, 8, 10]; + let ans = vec_loop(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); + } + + #[test] + fn test_vec_map_example() { + let input = [1, 2, 3]; + let ans = vec_map_example(&input); + assert_eq!(ans, [2, 3, 4]); + } + + #[test] + fn test_vec_map() { + let input = [2, 4, 6, 8, 10]; + let ans = vec_map(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); + } +} -- cgit v1.2.3 From 946c29679e27433ff455bdb30343551757d87769 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 16:16:52 +0200 Subject: move_semantics1 solution --- exercises/06_move_semantics/move_semantics1.rs | 3 +-- rustlings-macros/info.toml | 3 +-- solutions/06_move_semantics/move_semantics1.rs | 26 +++++++++++++++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/06_move_semantics/move_semantics1.rs b/exercises/06_move_semantics/move_semantics1.rs index 8c3fe3a..4eb3d61 100644 --- a/exercises/06_move_semantics/move_semantics1.rs +++ b/exercises/06_move_semantics/move_semantics1.rs @@ -1,3 +1,4 @@ +// TODO: Fix the compiler error in this function. fn fill_vec(vec: Vec) -> Vec { let vec = vec; @@ -17,9 +18,7 @@ mod tests { #[test] fn move_semantics1() { let vec0 = vec![22, 44, 66]; - let vec1 = fill_vec(vec0); - assert_eq!(vec1, vec![22, 44, 66, 88]); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 3edd1c6..bfe32cd 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -342,8 +342,7 @@ error on the line where we push an element to the vector, right? The fix for this is going to be adding one keyword, and the addition is NOT on the line where we push to the vector (where the error is). -Also: Try accessing `vec0` after having called `fill_vec()`. See what -happens!""" +Try accessing `vec0` after having called `fill_vec()`. See what happens!""" [[exercises]] name = "move_semantics2" diff --git a/solutions/06_move_semantics/move_semantics1.rs b/solutions/06_move_semantics/move_semantics1.rs index 4e18198..ac34e7a 100644 --- a/solutions/06_move_semantics/move_semantics1.rs +++ b/solutions/06_move_semantics/move_semantics1.rs @@ -1 +1,25 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn fill_vec(vec: Vec) -> Vec { + let mut vec = vec; + // ^^^ added + + vec.push(88); + + vec +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics1() { + let vec0 = vec![22, 44, 66]; + let vec1 = fill_vec(vec0); + // `vec0` can't be accessed anymore because it is moved to `fill_vec`. + assert_eq!(vec1, vec![22, 44, 66, 88]); + } +} -- cgit v1.2.3 From 68142aff7f439f3a797b4e97a275ca7800eebc45 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 17:02:50 +0200 Subject: move_semantics2 solution --- exercises/06_move_semantics/move_semantics2.rs | 8 +++---- rustlings-macros/info.toml | 14 ++++--------- solutions/06_move_semantics/move_semantics2.rs | 29 +++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 15 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/06_move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs index d087911..a3ab7a0 100644 --- a/exercises/06_move_semantics/move_semantics2.rs +++ b/exercises/06_move_semantics/move_semantics2.rs @@ -1,5 +1,3 @@ -// Make the test pass by finding a way to keep both Vecs separate! - fn fill_vec(vec: Vec) -> Vec { let mut vec = vec; @@ -16,13 +14,15 @@ fn main() { mod tests { use super::*; + // TODO: Make both vectors `vec0` and `vec1` accessible at the same time to + // fix the compiler error in the test. #[test] fn move_semantics2() { let vec0 = vec![22, 44, 66]; let vec1 = fill_vec(vec0); - assert_eq!(vec0, vec![22, 44, 66]); - assert_eq!(vec1, vec![22, 44, 66, 88]); + assert_eq!(vec0, [22, 44, 66]); + assert_eq!(vec1, [22, 44, 66, 88]); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index bfe32cd..fb0126c 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -352,16 +352,10 @@ When running this exercise for the first time, you'll notice an error about "borrow of moved value". In Rust, when an argument is passed to a function and it's not explicitly returned, you can't use the original variable anymore. We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's -being "moved" into `vec1`, meaning we can't access `vec0` anymore after the -fact. - -Rust provides a couple of different ways to mitigate this issue, feel free to -try them all: -1. You could make another, separate version of the data that's in `vec0` and - pass that to `fill_vec` instead. -2. Make `fill_vec` borrow its argument instead of taking ownership of it, - and then copy the data within the function (`vec.clone()`) in order to - return an owned `Vec`. +being "moved" into `vec1`, meaning we can't access `vec0` anymore. + +You could make another, separate version of the data that's in `vec0` and +pass it to `fill_vec` instead. """ [[exercises]] diff --git a/solutions/06_move_semantics/move_semantics2.rs b/solutions/06_move_semantics/move_semantics2.rs index 4e18198..7bcd33a 100644 --- a/solutions/06_move_semantics/move_semantics2.rs +++ b/solutions/06_move_semantics/move_semantics2.rs @@ -1 +1,28 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn fill_vec(vec: Vec) -> Vec { + let mut vec = vec; + + vec.push(88); + + vec +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics2() { + let vec0 = vec![22, 44, 66]; + + // Cloning `vec0` so that the clone is moved into `fill_vec`, not `vec0` + // itself. + let vec1 = fill_vec(vec0.clone()); + + assert_eq!(vec0, [22, 44, 66]); + assert_eq!(vec1, [22, 44, 66, 88]); + } +} -- cgit v1.2.3 From e4dbbbf5f5f5d4ea0ede1ead1f82108968e6cea6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 18:14:19 +0200 Subject: Remove move_semantics4, add rest of move_semantics solutions --- exercises/06_move_semantics/move_semantics4.rs | 31 ++++++++------------------ exercises/06_move_semantics/move_semantics5.rs | 31 +++++++++++++------------- exercises/06_move_semantics/move_semantics6.rs | 21 ----------------- rustlings-macros/info.toml | 27 +++++----------------- solutions/06_move_semantics/move_semantics4.rs | 22 +++++++++++++++++- solutions/06_move_semantics/move_semantics5.rs | 22 +++++++++++++++++- solutions/06_move_semantics/move_semantics6.rs | 1 - 7 files changed, 72 insertions(+), 83 deletions(-) delete mode 100644 exercises/06_move_semantics/move_semantics6.rs delete mode 100644 solutions/06_move_semantics/move_semantics6.rs (limited to 'rustlings-macros') diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs index b662224..c225f3b 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -1,31 +1,18 @@ -// Refactor this code so that instead of passing `vec0` into the `fill_vec` -// function, the Vector gets created in the function itself and passed back to -// the test function. - -// `fill_vec()` no longer takes `vec: Vec` as argument - don't change this! -fn fill_vec() -> Vec { - // Instead, let's create and fill the Vec in here - how do you do that? - let mut vec = vec; - - vec.push(88); - - vec -} - fn main() { // You can optionally experiment here. } #[cfg(test)] mod tests { - use super::*; - + // TODO: Fix the compiler errors only by reordering the lines in the test. + // Don't add, change or remove any line. #[test] - fn move_semantics4() { - let vec0 = vec![22, 44, 66]; - - let vec1 = fill_vec(vec0); - - assert_eq!(vec1, vec![22, 44, 66, 88]); + fn move_semantics5() { + let mut x = 100; + let y = &mut x; + let z = &mut x; + *y += 100; + *z += 1000; + assert_eq!(x, 1200); } } diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index b34560a..c9edf41 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -1,21 +1,22 @@ -// Make me compile only by reordering the lines in the test, but without adding, -// changing or removing any of them. +// TODO: Fix the compiler erros. Don't change anything except adding or removing +// references (the character `&`). fn main() { - // You can optionally experiment here. + let data = "Rust is great!".to_string(); + + get_char(data); + + string_uppercase(&data); +} + +// Shouldn't take ownership +fn get_char(data: String) -> char { + data.chars().last().unwrap() } -#[cfg(test)] -mod tests { - use super::*; +// Should take ownership +fn string_uppercase(mut data: &String) { + data = &data.to_uppercase(); - #[test] - fn move_semantics5() { - let mut x = 100; - let y = &mut x; - let z = &mut x; - *y += 100; - *z += 1000; - assert_eq!(x, 1200); - } + println!("{data}"); } diff --git a/exercises/06_move_semantics/move_semantics6.rs b/exercises/06_move_semantics/move_semantics6.rs deleted file mode 100644 index 2ad71db..0000000 --- a/exercises/06_move_semantics/move_semantics6.rs +++ /dev/null @@ -1,21 +0,0 @@ -// You can't change anything except adding or removing references. - -fn main() { - let data = "Rust is great!".to_string(); - - get_char(data); - - string_uppercase(&data); -} - -// Should not take ownership -fn get_char(data: String) -> char { - data.chars().last().unwrap() -} - -// Should take ownership -fn string_uppercase(mut data: &String) { - data = &data.to_uppercase(); - - println!("{}", data); -} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index fb0126c..d6236c5 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -371,28 +371,15 @@ an existing binding to be a mutable binding instead of an immutable one :)""" name = "move_semantics4" dir = "06_move_semantics" hint = """ -Stop reading whenever you feel like you have enough direction :) Or try -doing one step and then fixing the compiler errors that result! -So the end goal is to: - - get rid of the first line in main that creates the new vector - - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` - - `fill_vec` has had its signature changed, which our call should reflect - - since we're not creating a new vec in `main` anymore, we need to create - a new vec in `fill_vec`, and fill it with the expected values""" - -[[exercises]] -name = "move_semantics5" -dir = "06_move_semantics" -hint = """ Carefully reason about the range in which each mutable reference is in scope. Does it help to update the value of referent (`x`) immediately after -the mutable reference is taken? Read more about 'Mutable References' -in the book's section 'References and Borrowing': +the mutable reference is taken? +Read more about 'Mutable References' in the book's section 'References and Borrowing': https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. """ [[exercises]] -name = "move_semantics6" +name = "move_semantics5" dir = "06_move_semantics" test = false hint = """ @@ -401,14 +388,10 @@ https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html The first problem is that `get_char` is taking ownership of the string. So `data` is moved and can't be used for `string_uppercase`. `data` is moved to -`get_char` first, meaning that `string_uppercase` cannot manipulate the data. +`get_char` first, meaning that `string_uppercase` can't manipulate the data. Once you've fixed that, `string_uppercase`'s function signature will also need -to be adjusted. - -Can you figure out how? - -Another hint: it has to do with the `&` character.""" +to be adjusted.""" # STRUCTS diff --git a/solutions/06_move_semantics/move_semantics4.rs b/solutions/06_move_semantics/move_semantics4.rs index 4e18198..b7919ac 100644 --- a/solutions/06_move_semantics/move_semantics4.rs +++ b/solutions/06_move_semantics/move_semantics4.rs @@ -1 +1,21 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + // TODO: Fix the compiler errors only by reordering the lines in the test. + // Don't add, change or remove any line. + #[test] + fn move_semantics5() { + let mut x = 100; + let y = &mut x; + // `y` used here. + *y += 100; + // The mutable reference `y` is not used anymore, + // therefore a new reference can be created. + let z = &mut x; + *z += 1000; + assert_eq!(x, 1200); + } +} diff --git a/solutions/06_move_semantics/move_semantics5.rs b/solutions/06_move_semantics/move_semantics5.rs index 4e18198..1b3ca4e 100644 --- a/solutions/06_move_semantics/move_semantics5.rs +++ b/solutions/06_move_semantics/move_semantics5.rs @@ -1 +1,21 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + let data = "Rust is great!".to_string(); + + get_char(&data); + + string_uppercase(data); +} + +// Borrows instead of taking ownership. +// It is recommended to use `&str` instead of `&String` here. But this is +// enough for now because we didn't handle strings yet. +fn get_char(data: &String) -> char { + data.chars().last().unwrap() +} + +// Takes ownership instead of borrowing. +fn string_uppercase(mut data: String) { + data = data.to_uppercase(); + + println!("{data}"); +} diff --git a/solutions/06_move_semantics/move_semantics6.rs b/solutions/06_move_semantics/move_semantics6.rs deleted file mode 100644 index 4e18198..0000000 --- a/solutions/06_move_semantics/move_semantics6.rs +++ /dev/null @@ -1 +0,0 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 -- cgit v1.2.3 From ef842d3a946f477a32e26b9674cc5488cd629030 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 22:22:37 +0200 Subject: structs1 solution --- exercises/07_structs/structs1.rs | 25 ++++++++++---------- rustlings-macros/info.toml | 9 ++++---- solutions/07_structs/structs1.rs | 50 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 19 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/07_structs/structs1.rs b/exercises/07_structs/structs1.rs index 62f1421..959c4c6 100644 --- a/exercises/07_structs/structs1.rs +++ b/exercises/07_structs/structs1.rs @@ -1,13 +1,12 @@ -// Address all the TODOs to make the tests pass! - -struct ColorClassicStruct { - // TODO: Something goes here +struct ColorRegularStruct { + // TODO: Add the fields that the test `regular_structs` expects. + // What types should the fields have? What are the minimum and maximum values for RGB colors? } -struct ColorTupleStruct(/* TODO: Something goes here */); +struct ColorTupleStruct(/* TODO: Add the fields that the test `tuple_structs` expects */); #[derive(Debug)] -struct UnitLikeStruct; +struct UnitStruct; fn main() { // You can optionally experiment here. @@ -18,8 +17,8 @@ mod tests { use super::*; #[test] - fn classic_c_structs() { - // TODO: Instantiate a classic c struct! + fn regular_structs() { + // TODO: Instantiate a regular struct. // let green = assert_eq!(green.red, 0); @@ -29,7 +28,7 @@ mod tests { #[test] fn tuple_structs() { - // TODO: Instantiate a tuple struct! + // TODO: Instantiate a tuple struct. // let green = assert_eq!(green.0, 0); @@ -39,10 +38,10 @@ mod tests { #[test] fn unit_structs() { - // TODO: Instantiate a unit-like struct! - // let unit_like_struct = - let message = format!("{:?}s are fun!", unit_like_struct); + // TODO: Instantiate a unit struct. + // let unit_struct = + let message = format!("{unit_struct:?}s are fun!"); - assert_eq!(message, "UnitLikeStructs are fun!"); + assert_eq!(message, "UnitStructs are fun!"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index d6236c5..81b9895 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -402,15 +402,14 @@ hint = """ Rust has more than one type of struct. Three actually, all variants are used to package related data together. -There are normal (or classic) structs. These are named collections of related -data stored in fields. +There are regular structs. These are named collections of related data stored in +fields. Tuple structs are basically just named tuples. -Finally, Unit-like structs. These don't have any fields and are useful for -generics. +Finally, unit structs. These don't have any fields and are useful for generics. -In this exercise you need to complete and implement one of each kind. +In this exercise, you need to complete and implement one of each kind. Read more about structs in The Book: https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" diff --git a/solutions/07_structs/structs1.rs b/solutions/07_structs/structs1.rs index 4e18198..98fafcc 100644 --- a/solutions/07_structs/structs1.rs +++ b/solutions/07_structs/structs1.rs @@ -1 +1,49 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +struct ColorRegularStruct { + red: u8, + green: u8, + blue: u8, +} + +struct ColorTupleStruct(u8, u8, u8); + +#[derive(Debug)] +struct UnitStruct; + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn regular_structs() { + let green = ColorRegularStruct { + red: 0, + green: 255, + blue: 0, + }; + + assert_eq!(green.red, 0); + assert_eq!(green.green, 255); + assert_eq!(green.blue, 0); + } + + #[test] + fn tuple_structs() { + let green = ColorTupleStruct(0, 255, 0); + + assert_eq!(green.0, 0); + assert_eq!(green.1, 255); + assert_eq!(green.2, 0); + } + + #[test] + fn unit_structs() { + let unit_struct = UnitStruct; + let message = format!("{unit_struct:?}s are fun!"); + + assert_eq!(message, "UnitStructs are fun!"); + } +} -- cgit v1.2.3 From 1264510fc04de85efc0e2caf17aaa85354b6bffd Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 22:31:06 +0200 Subject: structs2 solution --- exercises/07_structs/structs2.rs | 4 ++-- rustlings-macros/info.toml | 2 +- solutions/07_structs/structs2.rs | 52 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 4 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/07_structs/structs2.rs b/exercises/07_structs/structs2.rs index 451dbe7..79141af 100644 --- a/exercises/07_structs/structs2.rs +++ b/exercises/07_structs/structs2.rs @@ -1,5 +1,3 @@ -// Address all the TODOs to make the tests pass! - #[derive(Debug)] struct Order { name: String, @@ -34,8 +32,10 @@ mod tests { #[test] fn your_order() { let order_template = create_order_template(); + // TODO: Create your own order using the update syntax and template above! // let your_order = + assert_eq!(your_order.name, "Hacker in Rust"); assert_eq!(your_order.year, order_template.year); assert_eq!(your_order.made_by_phone, order_template.made_by_phone); diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 81b9895..08bbd74 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -421,7 +421,7 @@ Creating instances of structs is easy, all you need to do is assign some values to its fields. There are however some shortcuts that can be taken when instantiating structs. -Have a look in The Book, to find out more: +Have a look in The Book to find out more: https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" [[exercises]] diff --git a/solutions/07_structs/structs2.rs b/solutions/07_structs/structs2.rs index 4e18198..589dd93 100644 --- a/solutions/07_structs/structs2.rs +++ b/solutions/07_structs/structs2.rs @@ -1 +1,51 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +#[derive(Debug)] +struct Order { + name: String, + year: u32, + made_by_phone: bool, + made_by_mobile: bool, + made_by_email: bool, + item_number: u32, + count: u32, +} + +fn create_order_template() -> Order { + Order { + name: String::from("Bob"), + year: 2019, + made_by_phone: false, + made_by_mobile: false, + made_by_email: true, + item_number: 123, + count: 0, + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn your_order() { + let order_template = create_order_template(); + + let your_order = Order { + name: String::from("Hacker in Rust"), + count: 1, + // Struct update syntax + ..order_template + }; + + assert_eq!(your_order.name, "Hacker in Rust"); + assert_eq!(your_order.year, order_template.year); + assert_eq!(your_order.made_by_phone, order_template.made_by_phone); + assert_eq!(your_order.made_by_mobile, order_template.made_by_mobile); + assert_eq!(your_order.made_by_email, order_template.made_by_email); + assert_eq!(your_order.item_number, order_template.item_number); + assert_eq!(your_order.count, 1); + } +} -- cgit v1.2.3 From d6fd251a73f2abd96662b09b32f718021568675c Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 22:54:00 +0200 Subject: structs3 solution --- exercises/07_structs/structs3.rs | 33 ++++++++-------- rustlings-macros/info.toml | 2 +- solutions/07_structs/structs3.rs | 84 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 18 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 10adb48..18a6cc9 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -1,6 +1,5 @@ // Structs contain data, but can also have logic. In this exercise we have -// defined the Package struct and we want to test some logic attached to it. -// Make the code compile and the tests pass! +// defined the `Package` struct and we want to test some logic attached to it. #[derive(Debug)] struct Package { @@ -10,26 +9,28 @@ struct Package { } impl Package { - fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Package { + fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self { if weight_in_grams < 10 { - // This is not how you should handle errors in Rust, - // but we will learn about error handling later. - panic!("Can not ship a package with weight below 10 grams.") - } else { - Package { - sender_country, - recipient_country, - weight_in_grams, - } + // This isn't how you should handle errors in Rust, but we will + // learn about error handling later. + panic!("Can't ship a package with weight below 10 grams"); + } + + Self { + sender_country, + recipient_country, + weight_in_grams, } } - fn is_international(&self) -> ??? { - // Something goes here... + // TODO: Add the correct return type to the function signature. + fn is_international(&self) { + // TODO: Read the tests that use this method to find out when a package is concidered international. } - fn get_fees(&self, cents_per_gram: u32) -> ??? { - // Something goes here... + // TODO: Add the correct return type to the function signature. + fn get_fees(&self, cents_per_gram: u32) { + // TODO: Calculate the package's fees. } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 08bbd74..7535b68 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -434,7 +434,7 @@ the places it goes through right? For `get_fees`: This method takes an additional argument, is there a field in the `Package` struct that this relates to? -Have a look in The Book, to find out more about method implementations: +Have a look in The Book to find out more about method implementations: https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" # ENUMS diff --git a/solutions/07_structs/structs3.rs b/solutions/07_structs/structs3.rs index 4e18198..3f878cc 100644 --- a/solutions/07_structs/structs3.rs +++ b/solutions/07_structs/structs3.rs @@ -1 +1,83 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +#[derive(Debug)] +struct Package { + sender_country: String, + recipient_country: String, + weight_in_grams: u32, +} + +impl Package { + fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self { + if weight_in_grams < 10 { + // This isn't how you should handle errors in Rust, but we will + // learn about error handling later. + panic!("Can't ship a package with weight below 10 grams"); + } + + Self { + sender_country, + recipient_country, + weight_in_grams, + } + } + + fn is_international(&self) -> bool { + // ^^^^^^^ added + self.sender_country != self.recipient_country + } + + fn get_fees(&self, cents_per_gram: u32) -> u32 { + // ^^^^^^ added + self.weight_in_grams * cents_per_gram + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[should_panic] + fn fail_creating_weightless_package() { + let sender_country = String::from("Spain"); + let recipient_country = String::from("Austria"); + + Package::new(sender_country, recipient_country, 5); + } + + #[test] + fn create_international_package() { + let sender_country = String::from("Spain"); + let recipient_country = String::from("Russia"); + + let package = Package::new(sender_country, recipient_country, 1200); + + assert!(package.is_international()); + } + + #[test] + fn create_local_package() { + let sender_country = String::from("Canada"); + let recipient_country = sender_country.clone(); + + let package = Package::new(sender_country, recipient_country, 1200); + + assert!(!package.is_international()); + } + + #[test] + fn calculate_transport_fees() { + let sender_country = String::from("Spain"); + let recipient_country = String::from("Spain"); + + let cents_per_gram = 3; + + let package = Package::new(sender_country, recipient_country, 1500); + + assert_eq!(package.get_fees(cents_per_gram), 4500); + assert_eq!(package.get_fees(cents_per_gram * 2), 9000); + } +} -- cgit v1.2.3 From 020711fa97e3be57f9e0098d6b9a329deec5a754 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 23:05:40 +0200 Subject: enums3 solution --- exercises/08_enums/enums2.rs | 2 +- rustlings-macros/info.toml | 2 +- solutions/08_enums/enums2.rs | 27 ++++++++++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs index f3b803f..14aa29a 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -1,6 +1,6 @@ #[derive(Debug)] enum Message { - // TODO: define the different variants used below + // TODO: Define the different variants used below. } impl Message { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 7535b68..a992104 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -451,7 +451,7 @@ dir = "08_enums" test = false hint = """ You can create enumerations that have different variants with different types -such as no data, anonymous structs, a single string, tuples, ...etc""" +such as no data, anonymous structs, a single string, tuples, etc.""" [[exercises]] name = "enums3" diff --git a/solutions/08_enums/enums2.rs b/solutions/08_enums/enums2.rs index 4e18198..13175dd 100644 --- a/solutions/08_enums/enums2.rs +++ b/solutions/08_enums/enums2.rs @@ -1 +1,26 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +#[derive(Debug)] +enum Message { + Move { x: i64, y: i64 }, + Echo(String), + ChangeColor(u8, u8, u8), + Quit, +} + +impl Message { + fn call(&self) { + println!("{:?}", self); + } +} + +fn main() { + let messages = [ + Message::Move { x: 10, y: 30 }, + Message::Echo(String::from("hello world")), + Message::ChangeColor(200, 255, 255), + Message::Quit, + ]; + + for message in &messages { + message.call(); + } +} -- cgit v1.2.3 From 2901d856627889e5a52dcf9f97d1c77032081c08 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 23:18:25 +0200 Subject: enums3 solution --- exercises/08_enums/enums3.rs | 19 ++++++----- rustlings-macros/info.toml | 6 ++-- solutions/08_enums/enums3.rs | 76 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 14 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index edac3df..7dd2171 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -1,7 +1,5 @@ -// Address all the TODOs to make the tests pass! - enum Message { - // TODO: implement the message variant types based on their usage below + // TODO: Implement the message variant types based on their usage below. } struct Point { @@ -26,17 +24,17 @@ impl State { } fn echo(&mut self, s: String) { - self.message = s + self.message = s; } - fn move_position(&mut self, p: Point) { - self.position = p; + fn move_position(&mut self, point: Point) { + self.position = point; } fn process(&mut self, message: Message) { - // TODO: create a match expression to process the different message variants + // TODO: Create a match expression to process the different message variants. // Remember: When passing a tuple as a function argument, you'll need extra parentheses: - // fn function((t, u, p, l, e)) + // e.g. `foo((t, u, p, l, e))` } } @@ -54,8 +52,9 @@ mod tests { quit: false, position: Point { x: 0, y: 0 }, color: (0, 0, 0), - message: "hello world".to_string(), + message: String::from("hello world"), }; + state.process(Message::ChangeColor(255, 0, 255)); state.process(Message::Echo(String::from("Hello world!"))); state.process(Message::Move(Point { x: 10, y: 15 })); @@ -64,7 +63,7 @@ mod tests { assert_eq!(state.color, (255, 0, 255)); assert_eq!(state.position.x, 10); assert_eq!(state.position.y, 15); - assert_eq!(state.quit, true); + assert!(state.quit); assert_eq!(state.message, "Hello world!"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index a992104..46a2c4b 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -457,12 +457,12 @@ such as no data, anonymous structs, a single string, tuples, etc.""" name = "enums3" dir = "08_enums" hint = """ -As a first step, you can define enums to compile this code without errors. +As a first step, define enums to compile the code without errors. -And then create a match expression in `process()`. +Then, create a match expression in `process()`. Note that you need to deconstruct some message variants in the match expression -to get value in the variant.""" +to get the variant's values.""" # STRINGS diff --git a/solutions/08_enums/enums3.rs b/solutions/08_enums/enums3.rs index 4e18198..8baa25c 100644 --- a/solutions/08_enums/enums3.rs +++ b/solutions/08_enums/enums3.rs @@ -1 +1,75 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +enum Message { + ChangeColor(u8, u8, u8), + Echo(String), + Move(Point), + Quit, +} + +struct Point { + x: u8, + y: u8, +} + +struct State { + color: (u8, u8, u8), + position: Point, + quit: bool, + message: String, +} + +impl State { + fn change_color(&mut self, color: (u8, u8, u8)) { + self.color = color; + } + + fn quit(&mut self) { + self.quit = true; + } + + fn echo(&mut self, s: String) { + self.message = s; + } + + fn move_position(&mut self, point: Point) { + self.position = point; + } + + fn process(&mut self, message: Message) { + match message { + Message::ChangeColor(r, g, b) => self.change_color((r, g, b)), + Message::Echo(s) => self.echo(s), + Message::Move(point) => self.move_position(point), + Message::Quit => self.quit(), + } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_match_message_call() { + let mut state = State { + quit: false, + position: Point { x: 0, y: 0 }, + color: (0, 0, 0), + message: String::from("hello world"), + }; + + state.process(Message::ChangeColor(255, 0, 255)); + state.process(Message::Echo(String::from("Hello world!"))); + state.process(Message::Move(Point { x: 10, y: 15 })); + state.process(Message::Quit); + + assert_eq!(state.color, (255, 0, 255)); + assert_eq!(state.position.x, 10); + assert_eq!(state.position.y, 15); + assert!(state.quit); + assert_eq!(state.message, "Hello world!"); + } +} -- cgit v1.2.3 From f574905b8e7d3b9320b2cb3a4c18e2039c9a771f Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 12:14:04 +0200 Subject: strings2 solution --- exercises/09_strings/strings2.rs | 12 ++++++------ rustlings-macros/info.toml | 2 +- solutions/09_strings/strings2.rs | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/09_strings/strings2.rs b/exercises/09_strings/strings2.rs index 4768278..93d9cb6 100644 --- a/exercises/09_strings/strings2.rs +++ b/exercises/09_strings/strings2.rs @@ -1,14 +1,14 @@ -// Make me compile without changing the function signature! +// TODO: Fix the compiler error in the `main` function without changing this function. +fn is_a_color_word(attempt: &str) -> bool { + attempt == "green" || attempt == "blue" || attempt == "red" +} fn main() { - let word = String::from("green"); // Try not changing this line :) + let word = String::from("green"); // Don't change this line. + if is_a_color_word(word) { println!("That is a color word I know!"); } else { println!("That is not a color word I know."); } } - -fn is_a_color_word(attempt: &str) -> bool { - attempt == "green" || attempt == "blue" || attempt == "red" -} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 46a2c4b..82206fc 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -486,7 +486,7 @@ dir = "09_strings" test = false hint = """ Yes, it would be really easy to fix this by just changing the value bound to -`word` to be a string slice instead of a `String`, wouldn't it?? There is a way +`word` to be a string slice instead of a `String`, wouldn't it? There is a way to add one character to the `if` statement, though, that will coerce the `String` into a string slice. diff --git a/solutions/09_strings/strings2.rs b/solutions/09_strings/strings2.rs index 4e18198..7de311f 100644 --- a/solutions/09_strings/strings2.rs +++ b/solutions/09_strings/strings2.rs @@ -1 +1,15 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn is_a_color_word(attempt: &str) -> bool { + attempt == "green" || attempt == "blue" || attempt == "red" +} + +fn main() { + let word = String::from("green"); + + if is_a_color_word(&word) { + // ^ added to have `&String` which is automatically + // coerced to `&str` by the compiler. + println!("That is a color word I know!"); + } else { + println!("That is not a color word I know."); + } +} -- cgit v1.2.3 From 613ec23f84d49078ed2e63c6111b7cf30ee764d6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 12:22:24 +0200 Subject: strings 3 solution --- exercises/09_strings/strings3.rs | 21 +++++++++-------- rustlings-macros/info.toml | 3 ++- solutions/09_strings/strings3.rs | 49 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 11 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs index f83a531..39fce18 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -1,16 +1,13 @@ -fn trim_me(input: &str) -> String { - // TODO: Remove whitespace from both ends of a string! - ??? +fn trim_me(input: &str) -> &str { + // TODO: Remove whitespace from both ends of a string. } fn compose_me(input: &str) -> String { - // TODO: Add " world!" to the string! There are multiple ways to do this! - ??? + // TODO: Add " world!" to the string! There are multiple ways to do this. } fn replace_me(input: &str) -> String { - // TODO: Replace "cars" in the string with "balloons"! - ??? + // TODO: Replace "cars" in the string with "balloons". } fn main() { @@ -36,7 +33,13 @@ mod tests { #[test] fn replace_a_string() { - assert_eq!(replace_me("I think cars are cool"), "I think balloons are cool"); - assert_eq!(replace_me("I love to look at cars"), "I love to look at balloons"); + assert_eq!( + replace_me("I think cars are cool"), + "I think balloons are cool", + ); + assert_eq!( + replace_me("I love to look at cars"), + "I love to look at balloons", + ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 82206fc..618fc91 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -499,7 +499,8 @@ https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercion name = "strings3" dir = "09_strings" hint = """ -There's tons of useful standard library functions for strings. Let's try and use some of them: +There are many useful standard library functions for strings. Let's try and use +some of them: https://doc.rust-lang.org/std/string/struct.String.html#method.trim For the `compose_me` method: You can either use the `format!` macro, or convert diff --git a/solutions/09_strings/strings3.rs b/solutions/09_strings/strings3.rs index 4e18198..a478e62 100644 --- a/solutions/09_strings/strings3.rs +++ b/solutions/09_strings/strings3.rs @@ -1 +1,48 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn trim_me(input: &str) -> &str { + input.trim() +} + +fn compose_me(input: &str) -> String { + // The macro `format!` has the same syntax as `println!`, but it returns a + // string instead of printing it to the terminal. + // Equivalent to `input.to_string() + " world!"` + format!("{input} world!") +} + +fn replace_me(input: &str) -> String { + input.replace("cars", "balloons") +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn trim_a_string() { + assert_eq!(trim_me("Hello! "), "Hello!"); + assert_eq!(trim_me(" What's up!"), "What's up!"); + assert_eq!(trim_me(" Hola! "), "Hola!"); + } + + #[test] + fn compose_a_string() { + assert_eq!(compose_me("Hello"), "Hello world!"); + assert_eq!(compose_me("Goodbye"), "Goodbye world!"); + } + + #[test] + fn replace_a_string() { + assert_eq!( + replace_me("I think cars are cool"), + "I think balloons are cool", + ); + assert_eq!( + replace_me("I love to look at cars"), + "I love to look at balloons", + ); + } +} -- cgit v1.2.3 From 879f0cd5c69b8b0bf93da036d31334f9757bf6a3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 12:51:21 +0200 Subject: strings4 solution --- exercises/09_strings/strings4.rs | 44 +++++++++++++++++++++++++--------------- rustlings-macros/info.toml | 10 ++++++++- solutions/09_strings/strings4.rs | 39 ++++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 18 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/09_strings/strings4.rs b/exercises/09_strings/strings4.rs index 1f3d88b..9d9eb48 100644 --- a/exercises/09_strings/strings4.rs +++ b/exercises/09_strings/strings4.rs @@ -1,24 +1,36 @@ -// Ok, here are a bunch of values - some are `String`s, some are `&str`s. Your -// task is to call one of these two functions on each value depending on what -// you think each value is. That is, add either `string_slice` or `string` -// before the parentheses on each line. If you're right, it will compile! +// Calls of this function should be replaced with calls of `string_slice` or `string`. +fn placeholder() {} fn string_slice(arg: &str) { - println!("{}", arg); + println!("{arg}"); } fn string(arg: String) { - println!("{}", arg); + println!("{arg}"); } +// TODO: Here are a bunch of values - some are `String`, some are `&str`. +// Your task is to replace `placeholder(…)` with either `string_slice(…)` +// or `string(…)` depending on what you think each value is. fn main() { - ???("blue"); - ???("red".to_string()); - ???(String::from("hi")); - ???("rust is fun!".to_owned()); - ???("nice weather".into()); - ???(format!("Interpolation {}", "Station")); - ???(&String::from("abc")[0..1]); - ???(" hello there ".trim()); - ???("Happy Monday!".to_string().replace("Mon", "Tues")); - ???("mY sHiFt KeY iS sTiCkY".to_lowercase()); + placeholder("blue"); + + placeholder("red".to_string()); + + placeholder(String::from("hi")); + + placeholder("rust is fun!".to_owned()); + + placeholder("nice weather".into()); + + placeholder(format!("Interpolation {}", "Station")); + + // WARNING: This is byte indexing, not character indexing. + // Character indexing can be done using `s.chars().nth(INDEX)`. + placeholder(&String::from("abc")[0..1]); + + placeholder(" hello there ".trim()); + + placeholder("Happy Monday!".replace("Mon", "Tues")); + + placeholder("mY sHiFt KeY iS sTiCkY".to_lowercase()); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 618fc91..7607650 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -510,7 +510,15 @@ the string slice into an owned string, which you can then freely extend.""" name = "strings4" dir = "09_strings" test = false -hint = "No hints this time ;)" +hint = """ +Replace `placeholder` with either `string` or `string_slice` in the `main` function. + +Example: +`placeholder("blue");` +should become +`string_slice("blue");` +because "blue" is `&str`, not `String`. +""" # MODULES diff --git a/solutions/09_strings/strings4.rs b/solutions/09_strings/strings4.rs index 4e18198..9dc6917 100644 --- a/solutions/09_strings/strings4.rs +++ b/solutions/09_strings/strings4.rs @@ -1 +1,38 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn string_slice(arg: &str) { + println!("{arg}"); +} +fn string(arg: String) { + println!("{arg}"); +} + +fn main() { + string_slice("blue"); + + string("red".to_string()); + + string(String::from("hi")); + + string("rust is fun!".to_owned()); + + // Here, both answers work. + // `.into()` converts a type into an expected type. + // If it is called where `String` is expected, it will convert `&str` to `String`. + // But if is called where `&str` is expected, then `&str` is kept `&str` since no + // conversion is needed. + string("nice weather".into()); + string_slice("nice weather".into()); + // ^^^^^^^ the compiler recommends removing the `.into()` + // call because it is a useless conversion. + + string(format!("Interpolation {}", "Station")); + + // WARNING: This is byte indexing, not character indexing. + // Character indexing can be done using `s.chars().nth(INDEX)`. + string_slice(&String::from("abc")[0..1]); + + string_slice(" hello there ".trim()); + + string("Happy Monday!".replace("Mon", "Tues")); + + string("mY sHiFt KeY iS sTiCkY".to_lowercase()); +} -- cgit v1.2.3 From ecbe9b7324364e94f7c6b4a4dd279fb90f5a938e Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 13:12:39 +0200 Subject: modules1 solution --- exercises/10_modules/modules1.rs | 1 + rustlings-macros/info.toml | 5 ++--- solutions/10_modules/modules1.rs | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/10_modules/modules1.rs b/exercises/10_modules/modules1.rs index 931a3e2..d97ab23 100644 --- a/exercises/10_modules/modules1.rs +++ b/exercises/10_modules/modules1.rs @@ -1,3 +1,4 @@ +// TODO: Fix the compiler error about calling a private function. mod sausage_factory { // Don't let anybody outside of this module see this! fn get_secret_recipe() -> String { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 7607650..97d7e07 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -527,9 +527,8 @@ name = "modules1" dir = "10_modules" test = false hint = """ -Everything is private in Rust by default-- but there's a keyword we can use -to make something public! The compiler error should point to the thing that -needs to be public.""" +Everything is private in Rust by default. But there's a keyword we can use +to make something public!""" [[exercises]] name = "modules2" diff --git a/solutions/10_modules/modules1.rs b/solutions/10_modules/modules1.rs index 4e18198..873b412 100644 --- a/solutions/10_modules/modules1.rs +++ b/solutions/10_modules/modules1.rs @@ -1 +1,15 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +mod sausage_factory { + fn get_secret_recipe() -> String { + String::from("Ginger") + } + + // Added `pub` before `fn` to make the function accessible outside the module. + pub fn make_sausage() { + get_secret_recipe(); + println!("sausage!"); + } +} + +fn main() { + sausage_factory::make_sausage(); +} -- cgit v1.2.3 From 98cd00de6378550985d819ac8cd1227c8a10818e Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 13:24:06 +0200 Subject: modules2 solution --- exercises/10_modules/modules2.rs | 19 +++++++++---------- rustlings-macros/info.toml | 11 ++++++----- solutions/10_modules/modules2.rs | 24 +++++++++++++++++++++++- 3 files changed, 38 insertions(+), 16 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/10_modules/modules2.rs b/exercises/10_modules/modules2.rs index 5f8b0d5..24dce41 100644 --- a/exercises/10_modules/modules2.rs +++ b/exercises/10_modules/modules2.rs @@ -1,20 +1,19 @@ // You can bring module paths into scopes and provide new names for them with -// the 'use' and 'as' keywords. Fix these 'use' statements to make the code -// compile. +// the `use` and `as` keywords. mod delicious_snacks { - // TODO: Fix these use statements - use self::fruits::PEAR as ??? - use self::veggies::CUCUMBER as ??? + // TODO: Add the follwing two `use` statements after fixing them. + // use self::fruits::PEAR as ???; + // use self::veggies::CUCUMBER as ???; mod fruits { - pub const PEAR: &'static str = "Pear"; - pub const APPLE: &'static str = "Apple"; + pub const PEAR: &str = "Pear"; + pub const APPLE: &str = "Apple"; } mod veggies { - pub const CUCUMBER: &'static str = "Cucumber"; - pub const CARROT: &'static str = "Carrot"; + pub const CUCUMBER: &str = "Cucumber"; + pub const CARROT: &str = "Carrot"; } } @@ -22,6 +21,6 @@ fn main() { println!( "favorite snacks: {} and {}", delicious_snacks::fruit, - delicious_snacks::veggie + delicious_snacks::veggie, ); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 97d7e07..ba414e3 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -535,12 +535,13 @@ name = "modules2" dir = "10_modules" test = false hint = """ -The delicious_snacks module is trying to present an external interface that is -different than its internal structure (the `fruits` and `veggies` modules and -associated constants). Complete the `use` statements to fit the uses in main and -find the one keyword missing for both constants. +The `delicious_snacks` module is trying to present an external interface that +is different than its internal structure (the `fruits` and `veggies` modules +and associated constants). Complete the `use` statements to fit the uses in +`main` and find the one keyword missing for both constants. -Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" +Learn more in The Book: +https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" [[exercises]] name = "modules3" diff --git a/solutions/10_modules/modules2.rs b/solutions/10_modules/modules2.rs index 4e18198..55c316d 100644 --- a/solutions/10_modules/modules2.rs +++ b/solutions/10_modules/modules2.rs @@ -1 +1,23 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +mod delicious_snacks { + // Added `pub` and used the expected alias after `as`. + pub use self::fruits::PEAR as fruit; + pub use self::veggies::CUCUMBER as veggie; + + mod fruits { + pub const PEAR: &str = "Pear"; + pub const APPLE: &str = "Apple"; + } + + mod veggies { + pub const CUCUMBER: &str = "Cucumber"; + pub const CARROT: &str = "Carrot"; + } +} + +fn main() { + println!( + "favorite snacks: {} and {}", + delicious_snacks::fruit, + delicious_snacks::veggie, + ); +} -- cgit v1.2.3 From 3d540ed946ee9fd522ba9ec26f68055f5c498317 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 13:35:54 +0200 Subject: modules3 solution --- exercises/10_modules/modules3.rs | 11 +++++------ rustlings-macros/info.toml | 2 +- solutions/10_modules/modules3.rs | 9 ++++++++- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/10_modules/modules3.rs b/exercises/10_modules/modules3.rs index eff24a9..691608d 100644 --- a/exercises/10_modules/modules3.rs +++ b/exercises/10_modules/modules3.rs @@ -1,10 +1,9 @@ -// You can use the 'use' keyword to bring module paths from modules from -// anywhere and especially from the Rust standard library into your scope. Bring -// SystemTime and UNIX_EPOCH from the std::time module. Bonus style points if -// you can do it with one line! +// You can use the `use` keyword to bring module paths from modules from +// anywhere and especially from the standard library into your scope. -// TODO: Complete this use statement -use ??? +// TODO: Bring `SystemTime` and `UNIX_EPOCH` from the `std::time` module into +// your scope. Bonus style points if you can do it with one line! +// use ???; fn main() { match SystemTime::now().duration_since(UNIX_EPOCH) { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index ba414e3..58c0cdd 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -550,7 +550,7 @@ test = false hint = """ `UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a `use` statement for these two to bring them into scope. You can use nested -paths or the glob operator to bring these two in using only one line.""" +paths to bring these two in using only one line.""" # HASHMAPS diff --git a/solutions/10_modules/modules3.rs b/solutions/10_modules/modules3.rs index 4e18198..99ff5a7 100644 --- a/solutions/10_modules/modules3.rs +++ b/solutions/10_modules/modules3.rs @@ -1 +1,8 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +use std::time::{SystemTime, UNIX_EPOCH}; + +fn main() { + match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), + Err(_) => panic!("SystemTime before UNIX EPOCH!"), + } +} -- cgit v1.2.3 From 5baa503bfc27fc691dbc292b46d37d25c17cffab Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 24 Jun 2024 13:20:50 +0200 Subject: hashmaps1 solution --- exercises/11_hashmaps/hashmaps1.rs | 11 +++++----- rustlings-macros/info.toml | 8 ++----- solutions/11_hashmaps/hashmaps1.rs | 43 +++++++++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 13 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/11_hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs index e646ed7..0df7000 100644 --- a/exercises/11_hashmaps/hashmaps1.rs +++ b/exercises/11_hashmaps/hashmaps1.rs @@ -1,20 +1,19 @@ // A basket of fruits in the form of a hash map needs to be defined. The key // represents the name of the fruit and the value represents how many of that -// particular fruit is in the basket. You have to put at least three different +// particular fruit is in the basket. You have to put at least 3 different // types of fruits (e.g apple, banana, mango) in the basket and the total count -// of all the fruits should be at least five. -// -// Make me compile and pass the tests! +// of all the fruits should be at least 5. use std::collections::HashMap; fn fruit_basket() -> HashMap { - let mut basket = // TODO: declare your hash map here. + // TODO: Declare the hash map. + // let mut basket = // Two bananas are already given for you :) basket.insert(String::from("banana"), 2); - // TODO: Put more fruits in your basket here. + // TODO: Put more fruits in your basket. basket } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 58c0cdd..cf70d4d 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -558,12 +558,8 @@ paths to bring these two in using only one line.""" name = "hashmaps1" dir = "11_hashmaps" hint = """ -Hint 1: Take a look at the return type of the function to figure out - the type for the `basket`. - -Hint 2: Number of fruits should be at least 5. And you have to put - at least three different types of fruits. -""" +The number of fruits should be at least 5 and you have to put at least 3 +different types of fruits.""" [[exercises]] name = "hashmaps2" diff --git a/solutions/11_hashmaps/hashmaps1.rs b/solutions/11_hashmaps/hashmaps1.rs index 4e18198..3a787c4 100644 --- a/solutions/11_hashmaps/hashmaps1.rs +++ b/solutions/11_hashmaps/hashmaps1.rs @@ -1 +1,42 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// A basket of fruits in the form of a hash map needs to be defined. The key +// represents the name of the fruit and the value represents how many of that +// particular fruit is in the basket. You have to put at least 3 different +// types of fruits (e.g apple, banana, mango) in the basket and the total count +// of all the fruits should be at least 5. + +use std::collections::HashMap; + +fn fruit_basket() -> HashMap { + // Declare the hash map. + let mut basket = HashMap::new(); + + // Two bananas are already given for you :) + basket.insert(String::from("banana"), 2); + + // Put more fruits in your basket. + basket.insert(String::from("apple"), 3); + basket.insert(String::from("mango"), 1); + + basket +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn at_least_three_types_of_fruits() { + let basket = fruit_basket(); + assert!(basket.len() >= 3); + } + + #[test] + fn at_least_five_fruits() { + let basket = fruit_basket(); + assert!(basket.values().sum::() >= 5); + } +} -- cgit v1.2.3 From fbc226a51043f7c9be4c414292d37d3ce97038fe Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 24 Jun 2024 16:50:03 +0200 Subject: hashmaps2 solution --- exercises/11_hashmaps/hashmaps2.rs | 23 ++++----- rustlings-macros/info.toml | 5 +- solutions/11_hashmaps/hashmaps2.rs | 96 +++++++++++++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 17 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index 05b7a87..b3691b6 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -6,8 +6,6 @@ // must add fruit to the basket so that there is at least one of each kind and // more than 11 in total - we have a lot of mouths to feed. You are not allowed // to insert any more of these fruits! -// -// Make me pass the tests! use std::collections::HashMap; @@ -21,7 +19,7 @@ enum Fruit { } fn fruit_basket(basket: &mut HashMap) { - let fruit_kinds = vec![ + let fruit_kinds = [ Fruit::Apple, Fruit::Banana, Fruit::Mango, @@ -46,12 +44,8 @@ mod tests { // Don't modify this function! fn get_fruit_basket() -> HashMap { - let mut basket = HashMap::::new(); - basket.insert(Fruit::Apple, 4); - basket.insert(Fruit::Mango, 2); - basket.insert(Fruit::Lychee, 5); - - basket + let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)]; + HashMap::from_iter(content) } #[test] @@ -81,7 +75,7 @@ mod tests { #[test] fn all_fruit_types_in_basket() { - let fruit_kinds = vec![ + let fruit_kinds = [ Fruit::Apple, Fruit::Banana, Fruit::Mango, @@ -91,11 +85,12 @@ mod tests { let mut basket = get_fruit_basket(); fruit_basket(&mut basket); + for fruit_kind in fruit_kinds { - let amount = basket - .get(&fruit_kind) - .expect(format!("Fruit kind {:?} was not found in basket", fruit_kind).as_str()); - assert_ne!(amount, &0); + let Some(amount) = basket.get(&fruit_kind) else { + panic!("Fruit kind {fruit_kind:?} was not found in basket"); + }; + assert!(*amount > 0); } } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index cf70d4d..0da573b 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -566,8 +566,9 @@ name = "hashmaps2" dir = "11_hashmaps" hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. -Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value -""" + +Learn more in The Book: +https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value""" [[exercises]] name = "hashmaps3" diff --git a/solutions/11_hashmaps/hashmaps2.rs b/solutions/11_hashmaps/hashmaps2.rs index 4e18198..a5e6ef9 100644 --- a/solutions/11_hashmaps/hashmaps2.rs +++ b/solutions/11_hashmaps/hashmaps2.rs @@ -1 +1,95 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// We're collecting different fruits to bake a delicious fruit cake. For this, +// we have a basket, which we'll represent in the form of a hash map. The key +// represents the name of each fruit we collect and the value represents how +// many of that particular fruit we have collected. Three types of fruits - +// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You +// must add fruit to the basket so that there is at least one of each kind and +// more than 11 in total - we have a lot of mouths to feed. You are not allowed +// to insert any more of these fruits! + +use std::collections::HashMap; + +#[derive(Hash, PartialEq, Eq, Debug)] +enum Fruit { + Apple, + Banana, + Mango, + Lychee, + Pineapple, +} + +fn fruit_basket(basket: &mut HashMap) { + let fruit_kinds = [ + Fruit::Apple, + Fruit::Banana, + Fruit::Mango, + Fruit::Lychee, + Fruit::Pineapple, + ]; + + for fruit in fruit_kinds { + // If fruit doesn't exist, insert it with some value. + basket.entry(fruit).or_insert(5); + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + // Don't modify this function! + fn get_fruit_basket() -> HashMap { + let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)]; + HashMap::from_iter(content) + } + + #[test] + fn test_given_fruits_are_not_modified() { + let mut basket = get_fruit_basket(); + fruit_basket(&mut basket); + assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4); + assert_eq!(*basket.get(&Fruit::Mango).unwrap(), 2); + assert_eq!(*basket.get(&Fruit::Lychee).unwrap(), 5); + } + + #[test] + fn at_least_five_types_of_fruits() { + let mut basket = get_fruit_basket(); + fruit_basket(&mut basket); + let count_fruit_kinds = basket.len(); + assert!(count_fruit_kinds >= 5); + } + + #[test] + fn greater_than_eleven_fruits() { + let mut basket = get_fruit_basket(); + fruit_basket(&mut basket); + let count = basket.values().sum::(); + assert!(count > 11); + } + + #[test] + fn all_fruit_types_in_basket() { + let fruit_kinds = [ + Fruit::Apple, + Fruit::Banana, + Fruit::Mango, + Fruit::Lychee, + Fruit::Pineapple, + ]; + + let mut basket = get_fruit_basket(); + fruit_basket(&mut basket); + + for fruit_kind in fruit_kinds { + let Some(amount) = basket.get(&fruit_kind) else { + panic!("Fruit kind {fruit_kind:?} was not found in basket"); + }; + assert!(*amount > 0); + } + } +} -- cgit v1.2.3 From f1bd4447924e797e8fb0012f6bc47a507438f3f5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 01:52:33 +0200 Subject: hashmaps3 solution --- exercises/11_hashmaps/hashmaps3.rs | 71 +++++++++++++++----------------- rustlings-macros/info.toml | 12 +++--- solutions/11_hashmaps/hashmaps3.rs | 84 +++++++++++++++++++++++++++++++++++++- 3 files changed, 122 insertions(+), 45 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 070c370..9f8fdd7 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -1,39 +1,38 @@ // A list of scores (one per line) of a soccer match is given. Each line is of -// the form : ",,," -// Example: England,France,4,2 (England scored 4 goals, France 2). +// the form ",,," +// Example: "England,France,4,2" (England scored 4 goals, France 2). // // You have to build a scores table containing the name of the team, the total // number of goals the team scored, and the total number of goals the team -// conceded. One approach to build the scores table is to use a Hashmap. -// The solution is partially written to use a Hashmap, -// complete it to pass the test. -// -// Make me pass the tests! +// conceded. use std::collections::HashMap; // A structure to store the goal details of a team. +#[derive(Default)] struct Team { goals_scored: u8, goals_conceded: u8, } -fn build_scores_table(results: String) -> HashMap { +fn build_scores_table(results: &str) -> HashMap<&str, Team> { // The name of the team is the key and its associated struct is the value. - let mut scores: HashMap = HashMap::new(); + let mut scores = HashMap::new(); + + for line in results.lines() { + let mut split_iterator = line.split(','); + // NOTE: We use `unwrap` because we didn't deal with error handling yet. + let team_1_name = split_iterator.next().unwrap(); + let team_2_name = split_iterator.next().unwrap(); + let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap(); + let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); - for r in results.lines() { - let v: Vec<&str> = r.split(',').collect(); - let team_1_name = v[0].to_string(); - let team_1_score: u8 = v[2].parse().unwrap(); - let team_2_name = v[1].to_string(); - let team_2_score: u8 = v[3].parse().unwrap(); - // TODO: Populate the scores table with details extracted from the - // current line. Keep in mind that goals scored by team_1 - // will be the number of goals conceded by team_2, and similarly - // goals scored by team_2 will be the number of goals conceded by - // team_1. + // TODO: Populate the scores table with the extracted details. + // Keep in mind that goals scored by team 1 will be the number of goals + // conceded by team 2. Similarly, goals scored by team 2 will be the + // number of goals conceded by team 1. } + scores } @@ -45,40 +44,34 @@ fn main() { mod tests { use super::*; - fn get_results() -> String { - let results = "".to_string() - + "England,France,4,2\n" - + "France,Italy,3,1\n" - + "Poland,Spain,2,0\n" - + "Germany,England,2,1\n"; - results - } + const RESULTS: &str = "England,France,4,2 +France,Italy,3,1 +Poland,Spain,2,0 +Germany,England,2,1 +England,Spain,1,0"; #[test] fn build_scores() { - let scores = build_scores_table(get_results()); + let scores = build_scores_table(RESULTS); - let mut keys: Vec<&String> = scores.keys().collect(); - keys.sort(); - assert_eq!( - keys, - vec!["England", "France", "Germany", "Italy", "Poland", "Spain"] - ); + assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"] + .into_iter() + .all(|team_name| scores.contains_key(team_name))); } #[test] fn validate_team_score_1() { - let scores = build_scores_table(get_results()); + let scores = build_scores_table(RESULTS); let team = scores.get("England").unwrap(); - assert_eq!(team.goals_scored, 5); + assert_eq!(team.goals_scored, 6); assert_eq!(team.goals_conceded, 4); } #[test] fn validate_team_score_2() { - let scores = build_scores_table(get_results()); + let scores = build_scores_table(RESULTS); let team = scores.get("Spain").unwrap(); assert_eq!(team.goals_scored, 0); - assert_eq!(team.goals_conceded, 2); + assert_eq!(team.goals_conceded, 3); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 0da573b..c5ce847 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -574,16 +574,18 @@ https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-va name = "hashmaps3" dir = "11_hashmaps" hint = """ -Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert - entries corresponding to each team in the scores table. +Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of + `HashMap` to insert the default value of `Team` if a team doesn't + exist in the table yet. -Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +Learn more in The Book: +https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value Hint 2: If there is already an entry for a given key, the value returned by `entry()` can be updated based on the existing value. -Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value -""" +Learn more in The Book: +https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value""" # QUIZ 2 diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index 4e18198..f4059bb 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -1 +1,83 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// A list of scores (one per line) of a soccer match is given. Each line is of +// the form ",,," +// Example: "England,France,4,2" (England scored 4 goals, France 2). +// +// You have to build a scores table containing the name of the team, the total +// number of goals the team scored, and the total number of goals the team +// conceded. + +use std::collections::HashMap; + +// A structure to store the goal details of a team. +#[derive(Default)] +struct Team { + goals_scored: u8, + goals_conceded: u8, +} + +fn build_scores_table(results: &str) -> HashMap<&str, Team> { + // The name of the team is the key and its associated struct is the value. + let mut scores = HashMap::new(); + + for line in results.lines() { + let mut split_iterator = line.split(','); + // NOTE: We use `unwrap` because we didn't deal with error handling yet. + let team_1_name = split_iterator.next().unwrap(); + let team_2_name = split_iterator.next().unwrap(); + let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap(); + let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); + + // Insert the default with zeros if a team doesn't exist yet. + let mut team_1 = scores.entry(team_1_name).or_insert_with(|| Team::default()); + // Update the values. + team_1.goals_scored += team_1_score; + team_1.goals_conceded += team_2_score; + + // Similarely for the second team. + let mut team_2 = scores.entry(team_2_name).or_insert_with(|| Team::default()); + team_2.goals_scored += team_2_score; + team_2.goals_conceded += team_1_score; + } + + scores +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + const RESULTS: &str = "England,France,4,2 +France,Italy,3,1 +Poland,Spain,2,0 +Germany,England,2,1 +England,Spain,1,0"; + + #[test] + fn build_scores() { + let scores = build_scores_table(RESULTS); + + assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"] + .into_iter() + .all(|team_name| scores.contains_key(team_name))); + } + + #[test] + fn validate_team_score_1() { + let scores = build_scores_table(RESULTS); + let team = scores.get("England").unwrap(); + assert_eq!(team.goals_scored, 6); + assert_eq!(team.goals_conceded, 4); + } + + #[test] + fn validate_team_score_2() { + let scores = build_scores_table(RESULTS); + let team = scores.get("Spain").unwrap(); + assert_eq!(team.goals_scored, 0); + assert_eq!(team.goals_conceded, 3); + } +} -- cgit v1.2.3 From c31e15c4cf5085adcf544a33ac256364fc2bcfbf Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 12:59:10 +0200 Subject: options1 solution --- exercises/12_options/options1.rs | 30 ++++++++++++++---------------- rustlings-macros/info.toml | 2 +- solutions/12_options/options1.rs | 39 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 18 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index b7cf7b0..5009f8b 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -1,12 +1,9 @@ // This function returns how much icecream there is left in the fridge. -// If it's before 10PM, there's 5 scoops left. At 10PM, someone eats it -// all, so there'll be no more left :( -fn maybe_icecream(time_of_day: u16) -> Option { - // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a - // value of 0. The Option output should gracefully handle cases where - // time_of_day > 23. - // TODO: Complete the function body - remember to return an Option! - ??? +// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, +// someone eats it all, so no icecream is left (value 0). Return `None` if +// `hour_of_day` is higher than 23. +fn maybe_icecream(hour_of_day: u16) -> Option { + // TODO: Complete the function body. } fn main() { @@ -17,6 +14,14 @@ fn main() { mod tests { use super::*; + #[test] + fn raw_value() { + // TODO: Fix this test. How do you get the value contained in the + // Option? + let icecreams = maybe_icecream(12); + assert_eq!(icecreams, 5); + } + #[test] fn check_icecream() { assert_eq!(maybe_icecream(0), Some(5)); @@ -24,14 +29,7 @@ mod tests { assert_eq!(maybe_icecream(18), Some(5)); assert_eq!(maybe_icecream(22), Some(0)); assert_eq!(maybe_icecream(23), Some(0)); + assert_eq!(maybe_icecream(24), None); assert_eq!(maybe_icecream(25), None); } - - #[test] - fn raw_value() { - // TODO: Fix this test. How do you get at the value contained in the - // Option? - let icecreams = maybe_icecream(12); - assert_eq!(icecreams, 5); - } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index c5ce847..3694b94 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -603,7 +603,7 @@ hint = """ Options can have a `Some` value, with an inner value, or a `None` value, without an inner value. -There's multiple ways to get at the inner value, you can use `unwrap`, or +There are multiple ways to get at the inner value, you can use `unwrap`, or pattern match. Unwrapping is the easiest, but how do you do it safely so that it doesn't panic in your face later?""" diff --git a/solutions/12_options/options1.rs b/solutions/12_options/options1.rs index 4e18198..1ffbb04 100644 --- a/solutions/12_options/options1.rs +++ b/solutions/12_options/options1.rs @@ -1 +1,38 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// This function returns how much icecream there is left in the fridge. +// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, +// someone eats it all, so no icecream is left (value 0). Return `None` if +// `hour_of_day` is higher than 23. +fn maybe_icecream(hour_of_day: u16) -> Option { + match hour_of_day { + 0..22 => Some(5), + 22..24 => Some(0), + _ => None, + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn raw_value() { + // Using `unwrap` is fine in a test. + let icecreams = maybe_icecream(12).unwrap(); + assert_eq!(icecreams, 5); + } + + #[test] + fn check_icecream() { + assert_eq!(maybe_icecream(0), Some(5)); + assert_eq!(maybe_icecream(9), Some(5)); + assert_eq!(maybe_icecream(18), Some(5)); + assert_eq!(maybe_icecream(22), Some(0)); + assert_eq!(maybe_icecream(23), Some(0)); + assert_eq!(maybe_icecream(24), None); + assert_eq!(maybe_icecream(25), None); + } +} -- cgit v1.2.3 From a91888e79e69e04e57c2049cdf940a70201e1d6e Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 14:35:05 +0200 Subject: option2 solution --- exercises/12_options/options2.rs | 10 +++++----- rustlings-macros/info.toml | 4 ++-- solutions/12_options/options2.rs | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 8 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs index 01f84c5..07c27c6 100644 --- a/exercises/12_options/options2.rs +++ b/exercises/12_options/options2.rs @@ -9,7 +9,7 @@ mod tests { let target = "rustlings"; let optional_target = Some(target); - // TODO: Make this an if let statement whose value is "Some" type + // TODO: Make this an if-let statement whose value is `Some`. word = optional_target { assert_eq!(word, target); } @@ -20,15 +20,15 @@ mod tests { let range = 10; let mut optional_integers: Vec> = vec![None]; - for i in 1..(range + 1) { + for i in 1..=range { optional_integers.push(Some(i)); } let mut cursor = range; - // TODO: make this a while let statement - remember that vector.pop also - // adds another layer of Option. You can stack `Option`s into - // while let and if let. + // TODO: Make this a while-let statement. Remember that `Vec::pop()` + // adds another layer of `Option`. You can do nested pattern matching + // in if-let and while-let statements. integer = optional_integers.pop() { assert_eq!(integer, cursor); cursor -= 1; diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 3694b94..6027b6b 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -616,9 +616,9 @@ Check out: - https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html - https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html -Remember that `Option`s can be stacked in `if let` and `while let`. +Remember that `Option`s can be nested in if-let and while-let statements. -For example: `Some(Some(variable)) = variable2` +For example: `if let Some(Some(x)) = y` Also see `Option::flatten` """ diff --git a/solutions/12_options/options2.rs b/solutions/12_options/options2.rs index 4e18198..0f24665 100644 --- a/solutions/12_options/options2.rs +++ b/solutions/12_options/options2.rs @@ -1 +1,37 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn simple_option() { + let target = "rustlings"; + let optional_target = Some(target); + + // if-let + if let Some(word) = optional_target { + assert_eq!(word, target); + } + } + + #[test] + fn layered_option() { + let range = 10; + let mut optional_integers: Vec> = vec![None]; + + for i in 1..=range { + optional_integers.push(Some(i)); + } + + let mut cursor = range; + + // while-let with nested pattern matching + while let Some(Some(integer)) = optional_integers.pop() { + assert_eq!(integer, cursor); + cursor -= 1; + } + + assert_eq!(cursor, 0); + } +} -- cgit v1.2.3 From 25b5686dd2ab2e3d5a228a71e9631c50ea50fffe Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 14:47:57 +0200 Subject: options3 solution --- exercises/12_options/options3.rs | 13 ++++++++----- rustlings-macros/info.toml | 3 ++- solutions/12_options/options3.rs | 27 ++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 7 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/12_options/options3.rs b/exercises/12_options/options3.rs index 5b70a79..4cedb51 100644 --- a/exercises/12_options/options3.rs +++ b/exercises/12_options/options3.rs @@ -1,14 +1,17 @@ +#[derive(Debug)] struct Point { x: i32, y: i32, } fn main() { - let y: Option = Some(Point { x: 100, y: 200 }); + let optional_point = Some(Point { x: 100, y: 200 }); - match y { - Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y), - _ => panic!("no match!"), + // TODO: Fix the compiler error by adding something to this match statement. + match optional_point { + Some(p) => println!("Co-ordinates are {},{}", p.x, p.y), + _ => panic!("No match!"), } - y; // Fix without deleting this line. + + println!("{optional_point:?}"); // Don't change this line. } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 6027b6b..5a47c85 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -631,7 +631,8 @@ hint = """ The compiler says a partial move happened in the `match` statement. How can this be avoided? The compiler shows the correction needed. -After making the correction as suggested by the compiler, do read: +After making the correction as suggested by the compiler, read the related docs +page: https://doc.rust-lang.org/std/keyword.ref.html""" # ERROR HANDLING diff --git a/solutions/12_options/options3.rs b/solutions/12_options/options3.rs index 4e18198..0081eeb 100644 --- a/solutions/12_options/options3.rs +++ b/solutions/12_options/options3.rs @@ -1 +1,26 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let optional_point = Some(Point { x: 100, y: 200 }); + + // Solution 1: Matching over the `Option` (not `&Option`) but without moving + // out of the `Some` variant. + match optional_point { + Some(ref p) => println!("Co-ordinates are {},{}", p.x, p.y), + // ^^^ added + _ => panic!("No match!"), + } + + // Solution 2: Matching over a reference (`&Option`) by added `&` before + // `optional_point`. + match &optional_point { + Some(p) => println!("Co-ordinates are {},{}", p.x, p.y), + _ => panic!("No match!"), + } + + println!("{optional_point:?}"); +} -- cgit v1.2.3 From 097f3c74ea16bad95a659fc41a494f24e07656d1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 15:06:29 +0200 Subject: errors1 solution --- exercises/13_error_handling/errors1.rs | 30 ++++++++++++++-------------- rustlings-macros/info.toml | 4 ++-- solutions/13_error_handling/errors1.rs | 36 +++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 18 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index e3e0482..6d9701b 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -1,22 +1,22 @@ -// This function refuses to generate text to be printed on a nametag if you pass -// it an empty string. It'd be nicer if it explained what the problem was, -// instead of just sometimes returning `None`. Thankfully, Rust has a similar -// construct to `Option` that can be used to express error conditions. Let's use -// it! - -fn main() { - // You can optionally experiment here. -} - +// TODO: This function refuses to generate text to be printed on a nametag if +// you pass it an empty string. It'd be nicer if it explained what the problem +// was instead of just returning `None`. Thankfully, Rust has a similar +// construct to `Option` that can be used to express error conditions. Change +// the function signature and body to return `Result` instead +// of `Option`. fn generate_nametag_text(name: String) -> Option { if name.is_empty() { // Empty names aren't allowed. None } else { - Some(format!("Hi! My name is {}", name)) + Some(format!("Hi! My name is {name}")) } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; @@ -24,17 +24,17 @@ mod tests { #[test] fn generates_nametag_text_for_a_nonempty_name() { assert_eq!( - generate_nametag_text("Beyoncé".into()), - Ok("Hi! My name is Beyoncé".into()) + generate_nametag_text("Beyoncé".to_string()).as_deref(), + Ok("Hi! My name is Beyoncé"), ); } #[test] fn explains_why_generating_nametag_text_fails() { assert_eq!( - generate_nametag_text("".into()), + generate_nametag_text(String::new()).as_deref(), // Don't change this line - Err("`name` was empty; it must be nonempty.".into()) + Err("`name` was empty; it must be nonempty."), ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5a47c85..3d8da58 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -647,8 +647,8 @@ is that `generate_nametag_text` should return a `Result` instead of an `Option`. To make this change, you'll need to: - update the return type in the function signature to be a `Result` that could be the variants `Ok(String)` and `Err(String)` - - change the body of the function to return `Ok(stuff)` where it currently - returns `Some(stuff)` + - change the body of the function to return `Ok(…)` where it currently + returns `Some(…)` - change the body of the function to return `Err(error message)` where it currently returns `None`""" diff --git a/solutions/13_error_handling/errors1.rs b/solutions/13_error_handling/errors1.rs index 4e18198..2a13bfd 100644 --- a/solutions/13_error_handling/errors1.rs +++ b/solutions/13_error_handling/errors1.rs @@ -1 +1,35 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn generate_nametag_text(name: String) -> Result { + // ^^^^^^ ^^^^^^ + if name.is_empty() { + // `Err(String)` instead of `None`. + Err("Empty names aren't allowed".to_string()) + } else { + // `Ok` instead of `Some`. + Ok(format!("Hi! My name is {name}")) + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn generates_nametag_text_for_a_nonempty_name() { + assert_eq!( + generate_nametag_text("Beyoncé".to_string()).as_deref(), + Ok("Hi! My name is Beyoncé"), + ); + } + + #[test] + fn explains_why_generating_nametag_text_fails() { + assert_eq!( + generate_nametag_text(String::new()).as_deref(), + Err("`name` was empty; it must be nonempty."), + ); + } +} -- cgit v1.2.3 From 050a23ce6763fedf0906cd1c04b76888aae12f7d Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 15:36:14 +0200 Subject: errors2 solution --- exercises/13_error_handling/errors2.rs | 19 ++++++----- rustlings-macros/info.toml | 7 ++-- solutions/13_error_handling/errors2.rs | 58 +++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 13 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index 345a0ee..e50a929 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -2,16 +2,16 @@ // 5 tokens, and whenever you purchase items there is a processing fee of 1 // token. A player of the game will type in how many items they want to buy, and // the `total_cost` function will calculate the total cost of the items. Since -// the player typed in the quantity, though, we get it as a string-- and they -// might have typed anything, not just numbers! +// the player typed in the quantity, we get it as a string. They might have +// typed anything, not just numbers! // // Right now, this function isn't handling the error case at all (and isn't -// handling the success case properly either). What we want to do is: if we call +// handling the success case properly either). What we want to do is: If we call // the `total_cost` function on a string that is not a number, that function -// will return a `ParseIntError`, and in that case, we want to immediately -// return that error from our function and not try to multiply and add. +// will return a `ParseIntError`. In that case, we want to immediately return +// that error from our function and not try to multiply and add. // -// There are at least two ways to implement this that are both correct-- but one +// There are at least two ways to implement this that are both correct. But one // is a lot shorter! use std::num::ParseIntError; @@ -19,6 +19,8 @@ use std::num::ParseIntError; fn total_cost(item_quantity: &str) -> Result { let processing_fee = 1; let cost_per_item = 5; + + // TODO: Handle the error case as described above. let qty = item_quantity.parse::(); Ok(qty * cost_per_item + processing_fee) @@ -31,6 +33,7 @@ fn main() { #[cfg(test)] mod tests { use super::*; + use std::num::IntErrorKind; #[test] fn item_quantity_is_a_valid_number() { @@ -40,8 +43,8 @@ mod tests { #[test] fn item_quantity_is_an_invalid_number() { assert_eq!( - total_cost("beep boop").unwrap_err().to_string(), - "invalid digit found in string" + total_cost("beep boop").unwrap_err().kind(), + &IntErrorKind::InvalidDigit, ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 3d8da58..2a4a24e 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -660,12 +660,11 @@ One way to handle this is using a `match` statement on `item_quantity.parse::()` where the cases are `Ok(something)` and `Err(something)`. -This pattern is very common in Rust, though, so there's a `?` operator that +This pattern is very common in Rust, though, so there's the `?` operator that does pretty much what you would make that match statement do for you! -Take a look at this section of the 'Error Handling' chapter: -https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator -and give it a try!""" +Take a look at this section of the "Error Handling" chapter: +https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator""" [[exercises]] name = "errors3" diff --git a/solutions/13_error_handling/errors2.rs b/solutions/13_error_handling/errors2.rs index 4e18198..de7c32b 100644 --- a/solutions/13_error_handling/errors2.rs +++ b/solutions/13_error_handling/errors2.rs @@ -1 +1,57 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// Say we're writing a game where you can buy items with tokens. All items cost +// 5 tokens, and whenever you purchase items there is a processing fee of 1 +// token. A player of the game will type in how many items they want to buy, and +// the `total_cost` function will calculate the total cost of the items. Since +// the player typed in the quantity, we get it as a string. They might have +// typed anything, not just numbers! +// +// Right now, this function isn't handling the error case at all (and isn't +// handling the success case properly either). What we want to do is: If we call +// the `total_cost` function on a string that is not a number, that function +// will return a `ParseIntError`. In that case, we want to immediately return +// that error from our function and not try to multiply and add. +// +// There are at least two ways to implement this that are both correct. But one +// is a lot shorter! + +use std::num::ParseIntError; + +fn total_cost(item_quantity: &str) -> Result { + let processing_fee = 1; + let cost_per_item = 5; + + // Added `?` to propagate the error. + let qty = item_quantity.parse::()?; + // ^ added + + // Equivalent to this verbose version: + let qty = match item_quantity.parse::() { + Ok(v) => v, + Err(e) => return Err(e), + }; + + Ok(qty * cost_per_item + processing_fee) +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + use std::num::IntErrorKind; + + #[test] + fn item_quantity_is_a_valid_number() { + assert_eq!(total_cost("34"), Ok(171)); + } + + #[test] + fn item_quantity_is_an_invalid_number() { + assert_eq!( + total_cost("beep boop").unwrap_err().kind(), + &IntErrorKind::InvalidDigit, + ); + } +} -- cgit v1.2.3 From c46d8bdf95c9a2025ee943feb208102a94b25ee6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 15:44:33 +0200 Subject: errors3 solution --- exercises/13_error_handling/errors3.rs | 21 ++++++++++++--------- rustlings-macros/info.toml | 4 ++-- solutions/13_error_handling/errors3.rs | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 12 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs index 2ef84f9..33a7b87 100644 --- a/exercises/13_error_handling/errors3.rs +++ b/exercises/13_error_handling/errors3.rs @@ -4,6 +4,17 @@ use std::num::ParseIntError; +// Don't change this function. +fn total_cost(item_quantity: &str) -> Result { + let processing_fee = 1; + let cost_per_item = 5; + let qty = item_quantity.parse::()?; + + Ok(qty * cost_per_item + processing_fee) +} + +// TODO: Fix the compiler error by changing the signature and body of the +// `main` function. fn main() { let mut tokens = 100; let pretend_user_input = "8"; @@ -14,14 +25,6 @@ fn main() { println!("You can't afford that many!"); } else { tokens -= cost; - println!("You now have {} tokens.", tokens); + println!("You now have {tokens} tokens."); } } - -fn total_cost(item_quantity: &str) -> Result { - let processing_fee = 1; - let cost_per_item = 5; - let qty = item_quantity.parse::()?; - - Ok(qty * cost_per_item + processing_fee) -} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 2a4a24e..74cb79d 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -675,8 +675,8 @@ If other functions can return a `Result`, why shouldn't `main`? It's a fairly common convention to return something like `Result<(), ErrorType>` from your `main` function. -The unit (`()`) type is there because nothing is really needed in terms of -positive results.""" +The unit type `()` is there because nothing is really needed in terms of a +positive result.""" [[exercises]] name = "errors4" diff --git a/solutions/13_error_handling/errors3.rs b/solutions/13_error_handling/errors3.rs index 4e18198..63f4aba 100644 --- a/solutions/13_error_handling/errors3.rs +++ b/solutions/13_error_handling/errors3.rs @@ -1 +1,32 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// This is a program that is trying to use a completed version of the +// `total_cost` function from the previous exercise. It's not working though! +// Why not? What should we do to fix it? + +use std::num::ParseIntError; + +// Don't change this function. +fn total_cost(item_quantity: &str) -> Result { + let processing_fee = 1; + let cost_per_item = 5; + let qty = item_quantity.parse::()?; + + Ok(qty * cost_per_item + processing_fee) +} + +fn main() -> Result<(), ParseIntError> { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ added + let mut tokens = 100; + let pretend_user_input = "8"; + + let cost = total_cost(pretend_user_input)?; + + if cost > tokens { + println!("You can't afford that many!"); + } else { + tokens -= cost; + println!("You now have {tokens} tokens."); + } + + // Added this line to return the `Ok` variant of the expected `Result`. + Ok(()) +} -- cgit v1.2.3 From 9b7a5c041e9856379154b109b2ee2f3e979d70f7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 15:54:18 +0200 Subject: errors4 solution --- exercises/13_error_handling/errors4.rs | 21 ++++++++++------- rustlings-macros/info.toml | 8 +++---- solutions/13_error_handling/errors4.rs | 43 +++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 15 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs index 993d42a..ba01e54 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -1,16 +1,16 @@ -#[derive(PartialEq, Debug)] -struct PositiveNonzeroInteger(u64); - #[derive(PartialEq, Debug)] enum CreationError { Negative, Zero, } +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + impl PositiveNonzeroInteger { - fn new(value: i64) -> Result { - // Hmm... Why is this always returning an Ok value? - Ok(PositiveNonzeroInteger(value as u64)) + fn new(value: i64) -> Result { + // TODO: This function shouldn't always return an `Ok`. + Ok(Self(value as u64)) } } @@ -24,11 +24,14 @@ mod tests { #[test] fn test_creation() { - assert!(PositiveNonzeroInteger::new(10).is_ok()); assert_eq!( + PositiveNonzeroInteger::new(10), + Ok(PositiveNonzeroInteger(10)), + ); + assert_eq!( + PositiveNonzeroInteger::new(-10), Err(CreationError::Negative), - PositiveNonzeroInteger::new(-10) ); - assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); + assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero)); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 74cb79d..d39044c 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -683,11 +683,9 @@ name = "errors4" dir = "13_error_handling" hint = """ `PositiveNonzeroInteger::new` is always creating a new instance and returning -an `Ok` result. - -It should be doing some checking, returning an `Err` result if those checks -fail, and only returning an `Ok` result if those checks determine that -everything is... okay :)""" +an `Ok` result. But it should be doing some checking, returning an `Err` if +those checks fail, and only returning an `Ok` if those checks determine that +everything is… okay :)""" [[exercises]] name = "errors5" diff --git a/solutions/13_error_handling/errors4.rs b/solutions/13_error_handling/errors4.rs index 4e18198..c43f493 100644 --- a/solutions/13_error_handling/errors4.rs +++ b/solutions/13_error_handling/errors4.rs @@ -1 +1,42 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + if value == 0 { + Err(CreationError::Zero) + } else if value < 0 { + Err(CreationError::Negative) + } else { + Ok(Self(value as u64)) + } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_creation() { + assert_eq!( + PositiveNonzeroInteger::new(10), + Ok(PositiveNonzeroInteger(10)), + ); + assert_eq!( + PositiveNonzeroInteger::new(-10), + Err(CreationError::Negative), + ); + assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero)); + } +} -- cgit v1.2.3 From 129884aff74964d13aba8309014554b5625d6e5b Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 18:21:19 +0200 Subject: errors5 solution --- exercises/13_error_handling/errors5.rs | 76 +++++++++++++++------------------- rustlings-macros/info.toml | 17 ++++---- solutions/13_error_handling/errors5.rs | 55 +++++++++++++++++++++++- 3 files changed, 96 insertions(+), 52 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs index 7192562..d0044db 100644 --- a/exercises/13_error_handling/errors5.rs +++ b/exercises/13_error_handling/errors5.rs @@ -1,38 +1,18 @@ -// This program uses an altered version of the code from errors4. -// -// This exercise uses some concepts that we won't get to until later in the -// course, like `Box` and the `From` trait. It's not important to understand -// them in detail right now, but you can read ahead if you like. For now, think -// of the `Box` type as an "I want anything that does ???" type, which, -// given Rust's usual standards for runtime safety, should strike you as -// somewhat lenient! +// This exercise is an altered version of the `errors4` exercise. It uses some +// concepts that we won't get to until later in the course, like `Box` and the +// `From` trait. It's not important to understand them in detail right now, but +// you can read ahead if you like. For now, think of the `Box` type as +// an "I want anything that does ???" type. // // In short, this particular use case for boxes is for when you want to own a // value and you care only that it is a type which implements a particular -// trait. To do so, The Box is declared as of type Box where Trait is -// the trait the compiler looks for on any value used in that context. For this -// exercise, that context is the potential errors which can be returned in a -// Result. -// -// What can we use to describe both errors? In other words, is there a trait -// which both errors implement? +// trait. To do so, The `Box` is declared as of type `Box` where +// `Trait` is the trait the compiler looks for on any value used in that +// context. For this exercise, that context is the potential errors which +// can be returned in a `Result`. -use std::error; +use std::error::Error; use std::fmt; -use std::num::ParseIntError; - -// TODO: update the return type of `main()` to make this compile. -fn main() -> Result<(), Box> { - let pretend_user_input = "42"; - let x: i64 = pretend_user_input.parse()?; - println!("output={:?}", PositiveNonzeroInteger::new(x)?); - Ok(()) -} - -// Don't change anything below this line. - -#[derive(PartialEq, Debug)] -struct PositiveNonzeroInteger(u64); #[derive(PartialEq, Debug)] enum CreationError { @@ -40,17 +20,7 @@ enum CreationError { Zero, } -impl PositiveNonzeroInteger { - fn new(value: i64) -> Result { - match value { - x if x < 0 => Err(CreationError::Negative), - x if x == 0 => Err(CreationError::Zero), - x => Ok(PositiveNonzeroInteger(x as u64)), - } - } -} - -// This is required so that `CreationError` can implement `error::Error`. +// This is required so that `CreationError` can implement `Error`. impl fmt::Display for CreationError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let description = match *self { @@ -61,4 +31,26 @@ impl fmt::Display for CreationError { } } -impl error::Error for CreationError {} +impl Error for CreationError {} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + 0 => Err(CreationError::Zero), + x if x < 0 => Err(CreationError::Negative), + x => Ok(PositiveNonzeroInteger(x as u64)), + } + } +} + +// TODO: Add the correct return type `Result<(), Box>`. What can we +// use to describe both errors? Is there a trait which both errors implement? +fn main() { + let pretend_user_input = "42"; + let x: i64 = pretend_user_input.parse()?; + println!("output={:?}", PositiveNonzeroInteger::new(x)?); + Ok(()) +} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index d39044c..700c179 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -692,24 +692,23 @@ name = "errors5" dir = "13_error_handling" test = false hint = """ -There are two different possible `Result` types produced within `main()`, which -are propagated using `?` operators. How do we declare a return type from -`main()` that allows both? +There are two different possible `Result` types produced within the `main` +function, which are propagated using the `?` operators. How do we declare a +return type for the `main` function that allows both? Under the hood, the `?` operator calls `From::from` on the error value to -convert it to a boxed trait object, a `Box`. This boxed trait -object is polymorphic, and since all errors implement the `error::Error` trait, -we can capture lots of different errors in one "Box" object. +convert it to a boxed trait object, a `Box`. This boxed trait object +is polymorphic, and since all errors implement the `Error` trait, we can capture +lots of different errors in one `Box` object. -Check out this section of the book: +Check out this section of The Book: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator Read more about boxing errors: https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html Read more about using the `?` operator with boxed errors: -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html -""" +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html""" [[exercises]] name = "errors6" diff --git a/solutions/13_error_handling/errors5.rs b/solutions/13_error_handling/errors5.rs index 4e18198..c1424ee 100644 --- a/solutions/13_error_handling/errors5.rs +++ b/solutions/13_error_handling/errors5.rs @@ -1 +1,54 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// This exercise is an altered version of the `errors4` exercise. It uses some +// concepts that we won't get to until later in the course, like `Box` and the +// `From` trait. It's not important to understand them in detail right now, but +// you can read ahead if you like. For now, think of the `Box` type as +// an "I want anything that does ???" type. +// +// In short, this particular use case for boxes is for when you want to own a +// value and you care only that it is a type which implements a particular +// trait. To do so, The `Box` is declared as of type `Box` where +// `Trait` is the trait the compiler looks for on any value used in that +// context. For this exercise, that context is the potential errors which +// can be returned in a `Result`. + +use std::error::Error; +use std::fmt; + +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +// This is required so that `CreationError` can implement `Error`. +impl fmt::Display for CreationError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let description = match *self { + CreationError::Negative => "number is negative", + CreationError::Zero => "number is zero", + }; + f.write_str(description) + } +} + +impl Error for CreationError {} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + x if x < 0 => Err(CreationError::Negative), + 0 => Err(CreationError::Zero), + x => Ok(PositiveNonzeroInteger(x as u64)), + } + } +} + +fn main() -> Result<(), Box> { + let pretend_user_input = "42"; + let x: i64 = pretend_user_input.parse()?; + println!("output={:?}", PositiveNonzeroInteger::new(x)?); + Ok(()) +} -- cgit v1.2.3 From b1daea1fe8536d7b7b4463cb8fc36d69848ef77a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 01:12:50 +0200 Subject: errors6 solution --- exercises/13_error_handling/errors6.rs | 71 +++++++++++++------------- rustlings-macros/info.toml | 10 ++-- solutions/13_error_handling/errors6.rs | 93 +++++++++++++++++++++++++++++++++- 3 files changed, 130 insertions(+), 44 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs index 8b08e08..0652abd 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -1,12 +1,18 @@ -// Using catch-all error types like `Box` isn't recommended -// for library code, where callers might want to make decisions based on the -// error content, instead of printing it out or propagating it further. Here, we -// define a custom error type to make it possible for callers to decide what to -// do next when our function returns an error. +// Using catch-all error types like `Box` isn't recommended for +// library code where callers might want to make decisions based on the error +// content instead of printing it out or propagating it further. Here, we define +// a custom error type to make it possible for callers to decide what to do next +// when our function returns an error. use std::num::ParseIntError; -// This is a custom error type that we will be using in `parse_pos_nonzero()`. +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +// A custom error type that we will be using in `PositiveNonzeroInteger::parse`. #[derive(PartialEq, Debug)] enum ParsePosNonzeroError { Creation(CreationError), @@ -14,39 +20,32 @@ enum ParsePosNonzeroError { } impl ParsePosNonzeroError { - fn from_creation(err: CreationError) -> ParsePosNonzeroError { - ParsePosNonzeroError::Creation(err) + fn from_creation(err: CreationError) -> Self { + Self::Creation(err) } - // TODO: add another error conversion function here. - // fn from_parseint... -} -fn parse_pos_nonzero(s: &str) -> Result { - // TODO: change this to return an appropriate error instead of panicking - // when `parse()` returns an error. - let x: i64 = s.parse().unwrap(); - PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation) + // TODO: Add another error conversion function here. + // fn from_parseint(???) -> Self { ??? } } -// Don't change anything below this line. - #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); -#[derive(PartialEq, Debug)] -enum CreationError { - Negative, - Zero, -} - impl PositiveNonzeroInteger { - fn new(value: i64) -> Result { + fn new(value: i64) -> Result { match value { x if x < 0 => Err(CreationError::Negative), x if x == 0 => Err(CreationError::Zero), - x => Ok(PositiveNonzeroInteger(x as u64)), + x => Ok(Self(x as u64)), } } + + fn parse(s: &str) -> Result { + // TODO: change this to return an appropriate error instead of panicking + // when `parse()` returns an error. + let x: i64 = s.parse().unwrap(); + Self::new(x).map_err(ParsePosNonzeroError::from_creation) + } } fn main() { @@ -56,36 +55,36 @@ fn main() { #[cfg(test)] mod test { use super::*; + use std::num::IntErrorKind; #[test] fn test_parse_error() { - // We can't construct a ParseIntError, so we have to pattern match. assert!(matches!( - parse_pos_nonzero("not a number"), - Err(ParsePosNonzeroError::ParseInt(_)) + PositiveNonzeroInteger::parse("not a number"), + Err(ParsePosNonzeroError::ParseInt(_)), )); } #[test] fn test_negative() { assert_eq!( - parse_pos_nonzero("-555"), - Err(ParsePosNonzeroError::Creation(CreationError::Negative)) + PositiveNonzeroInteger::parse("-555"), + Err(ParsePosNonzeroError::Creation(CreationError::Negative)), ); } #[test] fn test_zero() { assert_eq!( - parse_pos_nonzero("0"), - Err(ParsePosNonzeroError::Creation(CreationError::Zero)) + PositiveNonzeroInteger::parse("0"), + Err(ParsePosNonzeroError::Creation(CreationError::Zero)), ); } #[test] fn test_positive() { - let x = PositiveNonzeroInteger::new(42); - assert!(x.is_ok()); - assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap())); + let x = PositiveNonzeroInteger::new(42).unwrap(); + assert_eq!(x.0, 42); + assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x)); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 700c179..dc288c0 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -714,17 +714,13 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen name = "errors6" dir = "13_error_handling" hint = """ -This exercise uses a completed version of `PositiveNonzeroInteger` from -errors4. +This exercise uses a completed version of `PositiveNonzeroInteger` from the +previous exercises. Below the line that `TODO` asks you to change, there is an example of using the `map_err()` method on a `Result` to transform one type of error into another. Try using something similar on the `Result` from `parse()`. You -might use the `?` operator to return early from the function, or you might -use a `match` expression, or maybe there's another way! - -You can create another function inside `impl ParsePosNonzeroError` to use -with `map_err()`. +can then use the `?` operator to return early. Read more about `map_err()` in the `std::result` documentation: https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs index 4e18198..70680cf 100644 --- a/solutions/13_error_handling/errors6.rs +++ b/solutions/13_error_handling/errors6.rs @@ -1 +1,92 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// Using catch-all error types like `Box` isn't recommended for +// library code where callers might want to make decisions based on the error +// content instead of printing it out or propagating it further. Here, we define +// a custom error type to make it possible for callers to decide what to do next +// when our function returns an error. + +use std::num::ParseIntError; + +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +// A custom error type that we will be using in `PositiveNonzeroInteger::parse`. +#[derive(PartialEq, Debug)] +enum ParsePosNonzeroError { + Creation(CreationError), + ParseInt(ParseIntError), +} + +impl ParsePosNonzeroError { + fn from_creation(err: CreationError) -> Self { + Self::Creation(err) + } + + fn from_parseint(err: ParseIntError) -> Self { + Self::ParseInt(err) + } +} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + x if x < 0 => Err(CreationError::Negative), + x if x == 0 => Err(CreationError::Zero), + x => Ok(Self(x as u64)), + } + } + + fn parse(s: &str) -> Result { + // Return an appropriate error instead of panicking when `parse()` + // returns an error. + let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?; + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Self::new(x).map_err(ParsePosNonzeroError::from_creation) + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod test { + use super::*; + use std::num::IntErrorKind; + + #[test] + fn test_parse_error() { + assert!(matches!( + PositiveNonzeroInteger::parse("not a number"), + Err(ParsePosNonzeroError::ParseInt(_)), + )); + } + + #[test] + fn test_negative() { + assert_eq!( + PositiveNonzeroInteger::parse("-555"), + Err(ParsePosNonzeroError::Creation(CreationError::Negative)), + ); + } + + #[test] + fn test_zero() { + assert_eq!( + PositiveNonzeroInteger::parse("0"), + Err(ParsePosNonzeroError::Creation(CreationError::Zero)), + ); + } + + #[test] + fn test_positive() { + let x = PositiveNonzeroInteger::new(42).unwrap(); + assert_eq!(x.0, 42); + assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x)); + } +} -- cgit v1.2.3 From 46121b71cf2f4da296e80fad025eaee03c67dcd5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 02:00:08 +0200 Subject: generics1 rewrite and solution --- exercises/14_generics/generics1.rs | 19 +++++++++++++++---- rustlings-macros/info.toml | 7 ++++++- solutions/14_generics/generics1.rs | 18 +++++++++++++++++- 3 files changed, 38 insertions(+), 6 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/14_generics/generics1.rs b/exercises/14_generics/generics1.rs index c023e64..87ed990 100644 --- a/exercises/14_generics/generics1.rs +++ b/exercises/14_generics/generics1.rs @@ -1,7 +1,18 @@ -// This shopping list program isn't compiling! Use your knowledge of generics to -// fix it. +// `Vec` is generic over the type `T`. In most cases, the compiler is able to +// infer `T`, for example after pushing a value with a concrete type to the vector. +// But in this exercise, the compiler needs some help through a type annotation. fn main() { - let mut shopping_list: Vec = Vec::new(); - shopping_list.push("milk"); + // TODO: Fix the compiler error by annotating the type of the vector + // `Vec`. Choose `T` as some integer type that can be created from + // `u8` and `i8`. + let mut numbers = Vec::new(); + + // Don't change the lines below. + let n1: u8 = 42; + numbers.push(n1.into()); + let n2: i8 = -1; + numbers.push(n2.into()); + + println!("{numbers:?}"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index dc288c0..23eb304 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -734,8 +734,13 @@ test = false hint = """ Vectors in Rust make use of generics to create dynamically sized arrays of any type. +If the vector `numbers` has the type `Vec`, then we can only push values of +type `T` to it. By using `into()` before pushing, we ask the compiler to convert +`n1` and `n2` to `T`. But the compiler doesn't know what `T` is yet and needs a +type annotation. -You need to tell the compiler what type we are pushing onto this vector.""" +`u8` and `i8` can both be converted to `i16`, `i32` and `i64`. Choose one for +the generic of the vector.""" [[exercises]] name = "generics2" diff --git a/solutions/14_generics/generics1.rs b/solutions/14_generics/generics1.rs index 4e18198..e2195fd 100644 --- a/solutions/14_generics/generics1.rs +++ b/solutions/14_generics/generics1.rs @@ -1 +1,17 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// `Vec` is generic over the type `T`. In most cases, the compiler is able to +// infer `T`, for example after pushing a value with a concrete type to the vector. +// But in this exercise, the compiler needs some help through a type annotation. + +fn main() { + // `u8` and `i8` can both be converted to `i16`. + let mut numbers: Vec = Vec::new(); + // ^^^^^^^^^^ added + + // Don't change the lines below. + let n1: u8 = 42; + numbers.push(n1.into()); + let n2: i8 = -1; + numbers.push(n2.into()); + + println!("{numbers:?}"); +} -- cgit v1.2.3 From de3f846a53055bbca5ec9dd6d536a31c82d39648 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 02:25:11 +0200 Subject: generics2 solution --- exercises/14_generics/generics2.rs | 4 ++-- rustlings-macros/info.toml | 8 ++------ solutions/14_generics/generics2.rs | 29 ++++++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 9 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/14_generics/generics2.rs b/exercises/14_generics/generics2.rs index 6cdcdaf..8908725 100644 --- a/exercises/14_generics/generics2.rs +++ b/exercises/14_generics/generics2.rs @@ -1,10 +1,10 @@ // This powerful wrapper provides the ability to store a positive integer value. -// Rewrite it using generics so that it supports wrapping ANY type. - +// TODO: Rewrite it using a generic so that it supports wrapping ANY type. struct Wrapper { value: u32, } +// TODO: Adapt the struct's implementation to be generic over the wrapped value. impl Wrapper { fn new(value: u32) -> Self { Wrapper { value } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 23eb304..11d6d59 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -746,12 +746,8 @@ the generic of the vector.""" name = "generics2" dir = "14_generics" hint = """ -Currently we are wrapping only values of type `u32`. - -Maybe we could update the explicit references to this data type somehow? - -If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions -""" +Related section in The Book: +https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions""" # TRAITS diff --git a/solutions/14_generics/generics2.rs b/solutions/14_generics/generics2.rs index 4e18198..14f3f7a 100644 --- a/solutions/14_generics/generics2.rs +++ b/solutions/14_generics/generics2.rs @@ -1 +1,28 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +struct Wrapper { + value: T, +} + +impl Wrapper { + fn new(value: T) -> Self { + Wrapper { value } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn store_u32_in_wrapper() { + assert_eq!(Wrapper::new(42).value, 42); + } + + #[test] + fn store_str_in_wrapper() { + assert_eq!(Wrapper::new("Foo").value, "Foo"); + } +} -- cgit v1.2.3 From 091e1e7f7a1afad539479674e06cae7c8d8dab7f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 11:58:44 +0200 Subject: traits2 solution --- exercises/15_traits/traits2.rs | 13 ++++--------- rustlings-macros/info.toml | 15 ++++++--------- solutions/15_traits/traits2.rs | 28 +++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 19 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs index 170779b..e904016 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -1,14 +1,9 @@ -// Your task is to implement the trait `AppendBar` for a vector of strings. To -// implement this trait, consider for a moment what it means to 'append "Bar"' -// to a vector of strings. -// -// No boiler plate code this time, you can do this! - trait AppendBar { fn append_bar(self) -> Self; } -// TODO: Implement trait `AppendBar` for a vector of strings. +// TODO: Implement the trait `AppendBar` for a vector of strings. +// `appned_bar` should push the string "Bar" into the vector. fn main() { // You can optionally experiment here. @@ -21,7 +16,7 @@ mod tests { #[test] fn is_vec_pop_eq_bar() { let mut foo = vec![String::from("Foo")].append_bar(); - assert_eq!(foo.pop().unwrap(), String::from("Bar")); - assert_eq!(foo.pop().unwrap(), String::from("Foo")); + assert_eq!(foo.pop().unwrap(), "Bar"); + assert_eq!(foo.pop().unwrap(), "Foo"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 2cc1db6..604f674 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -755,21 +755,18 @@ https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions" name = "traits1" dir = "15_traits" hint = """ -A discussion about Traits in Rust can be found at: -https://doc.rust-lang.org/book/ch10-02-traits.html -""" +More about traits in The Book: +https://doc.rust-lang.org/book/ch10-02-traits.html""" [[exercises]] name = "traits2" dir = "15_traits" hint = """ -Notice how the trait takes ownership of `self`, and returns `Self`. - -Try mutating the incoming string vector. Have a look at the tests to see -what the result should look like! +Notice how the trait takes ownership of `self` and returns `Self`. -Vectors provide suitable methods for adding an element at the end. See -the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" +Although the signature of `append_bar` in the trait takes `self` as argument, +the implementation can take `mut self` instead. This is possible because the +the value is owned anyway.""" [[exercises]] name = "traits3" diff --git a/solutions/15_traits/traits2.rs b/solutions/15_traits/traits2.rs index 4e18198..0db93e0 100644 --- a/solutions/15_traits/traits2.rs +++ b/solutions/15_traits/traits2.rs @@ -1 +1,27 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +trait AppendBar { + fn append_bar(self) -> Self; +} + +impl AppendBar for Vec { + fn append_bar(mut self) -> Self { + // ^^^ this is important + self.push(String::from("Bar")); + self + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_vec_pop_eq_bar() { + let mut foo = vec![String::from("Foo")].append_bar(); + assert_eq!(foo.pop().unwrap(), "Bar"); + assert_eq!(foo.pop().unwrap(), "Foo"); + } +} -- cgit v1.2.3 From c07209b635d6adf6fcadcbcca14942bf76a0a978 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:00:28 +0200 Subject: Unify info.toml --- rustlings-macros/info.toml | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) (limited to 'rustlings-macros') diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 604f674..f5dd82a 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -776,8 +776,7 @@ Traits can have a default implementation for functions. Structs that implement the trait can then use the default version of these functions if they choose not to implement the function themselves. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations -""" +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations""" [[exercises]] name = "traits4" @@ -786,8 +785,7 @@ hint = """ Instead of using concrete types as parameters you can use traits. Try replacing the '??' with 'impl [what goes here?]' -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters -""" +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters""" [[exercises]] name = "traits5" @@ -797,8 +795,7 @@ hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the '??' with 'impl [what goes here?] + [what goes here?]'. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax -""" +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax""" # QUIZ 3 @@ -913,8 +910,7 @@ Step 4: An iterator goes through all elements in a collection, but what if we've run out of elements? What should we expect here? If you're stuck, take a look at -https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. -""" +https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas.""" [[exercises]] name = "iterators2" @@ -1008,8 +1004,7 @@ assertions). For a non-empty list keep in mind that we want to use our `Cons` "list builder". Although the current list is one of integers (`i32`), feel free to change the -definition and try other types! -""" +definition and try other types!""" [[exercises]] name = "rc1" @@ -1026,8 +1021,7 @@ In the end the `Sun` only has one reference again, to itself. See more at: https://doc.rust-lang.org/book/ch15-04-rc.html -* Unfortunately Pluto is no longer considered a planet :( -""" +* Unfortunately Pluto is no longer considered a planet :(""" [[exercises]] name = "arc1" @@ -1042,10 +1036,9 @@ inside the loop but still in the main thread. thread-local copy of the numbers. This is a simple exercise if you understand the underlying concepts, but if this -is too much of a struggle, consider reading through all of Chapter 16 in the -book: -https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html -""" +is too much of a struggle, consider reading through all of Chapter 16 in The +Book: +https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html""" [[exercises]] name = "cow1" @@ -1055,8 +1048,7 @@ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is called. Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation -on the `Cow` type. -""" +on the `Cow` type.""" # THREADS @@ -1075,8 +1067,7 @@ https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-f Use the `JoinHandle`s to wait for each thread to finish and collect their results. -https://doc.rust-lang.org/std/thread/struct.JoinHandle.html -""" +https://doc.rust-lang.org/std/thread/struct.JoinHandle.html""" [[exercises]] name = "threads2" @@ -1097,8 +1088,7 @@ let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); ``` Similar to the code in the following example in the book: -https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads -""" +https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads""" [[exercises]] name = "threads3" @@ -1113,8 +1103,7 @@ one thread and receive them in another. Multiple producers are possible by using clone() to create a duplicate of the original sending end. -See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. -""" +See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info.""" # MACROS @@ -1230,8 +1219,7 @@ or a closure to wrap the error from `parse::`. Yet another hint: If you would like to propagate errors by using the `?` operator in your solution, you might want to look at -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html -""" +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html""" [[exercises]] name = "try_from_into" -- cgit v1.2.3 From b4b7ae63ada9128d4798d301cfc757a60904c6b8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:11:57 +0200 Subject: traits3 solution --- exercises/15_traits/traits3.rs | 15 +++++++-------- rustlings-macros/info.toml | 9 +++++---- solutions/15_traits/traits3.rs | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 13 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index 66da235..c244650 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -1,9 +1,8 @@ -// Your task is to implement the Licensed trait for both structures and have -// them return the same information without writing the same function twice. -// -// Consider what you can add to the Licensed trait. - trait Licensed { + // TODO: Add a default implementation for `licensing_info` so that + // implementors like the two structs below can share that default behavior + // without repeating the function. + // The default license information should be the string "Default license". fn licensing_info(&self) -> String; } @@ -15,8 +14,8 @@ struct OtherSoftware { version_number: String, } -impl Licensed for SomeSoftware {} // Don't edit this line -impl Licensed for OtherSoftware {} // Don't edit this line +impl Licensed for SomeSoftware {} // Don't edit this line. +impl Licensed for OtherSoftware {} // Don't edit this line. fn main() { // You can optionally experiment here. @@ -28,7 +27,7 @@ mod tests { #[test] fn is_licensing_info_the_same() { - let licensing_info = String::from("Some information"); + let licensing_info = "Default license"; let some_software = SomeSoftware { version_number: 1 }; let other_software = OtherSoftware { version_number: "v2.0.0".to_string(), diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index f5dd82a..92e440a 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -772,11 +772,12 @@ the value is owned anyway.""" name = "traits3" dir = "15_traits" hint = """ -Traits can have a default implementation for functions. Structs that implement -the trait can then use the default version of these functions if they choose not -to implement the function themselves. +Traits can have a default implementation for functions. Data types that +implement the trait can then use the default version of these functions +if they choose not to implement the function themselves. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations""" +Related section in The Book: +https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations""" [[exercises]] name = "traits4" diff --git a/solutions/15_traits/traits3.rs b/solutions/15_traits/traits3.rs index 4e18198..3d8ec85 100644 --- a/solutions/15_traits/traits3.rs +++ b/solutions/15_traits/traits3.rs @@ -1 +1,36 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +trait Licensed { + fn licensing_info(&self) -> String { + "Default license".to_string() + } +} + +struct SomeSoftware { + version_number: i32, +} + +struct OtherSoftware { + version_number: String, +} + +impl Licensed for SomeSoftware {} +impl Licensed for OtherSoftware {} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_licensing_info_the_same() { + let licensing_info = "Default license"; + let some_software = SomeSoftware { version_number: 1 }; + let other_software = OtherSoftware { + version_number: "v2.0.0".to_string(), + }; + assert_eq!(some_software.licensing_info(), licensing_info); + assert_eq!(other_software.licensing_info(), licensing_info); + } +} -- cgit v1.2.3 From c0452d160b889b3686409820192797d9e9f9cad7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:23:33 +0200 Subject: traits4 solution --- exercises/15_traits/traits4.rs | 27 ++++++++------------------- rustlings-macros/info.toml | 5 +++-- solutions/15_traits/traits4.rs | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 22 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs index ed63f6e..80092a6 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -1,23 +1,18 @@ -// Your task is to replace the '??' sections so the code compiles. -// -// Don't change any line other than the marked one. - trait Licensed { fn licensing_info(&self) -> String { - "some information".to_string() + "Default license".to_string() } } -struct SomeSoftware {} - -struct OtherSoftware {} +struct SomeSoftware; +struct OtherSoftware; impl Licensed for SomeSoftware {} impl Licensed for OtherSoftware {} -// YOU MAY ONLY CHANGE THE NEXT LINE -fn compare_license_types(software: ??, software_two: ??) -> bool { - software.licensing_info() == software_two.licensing_info() +// TODO: Fix the compiler error by only changing the signature of this function. +fn compare_license_types(software1: ???, software2: ???) -> bool { + software1.licensing_info() == software2.licensing_info() } fn main() { @@ -30,17 +25,11 @@ mod tests { #[test] fn compare_license_information() { - let some_software = SomeSoftware {}; - let other_software = OtherSoftware {}; - - assert!(compare_license_types(some_software, other_software)); + assert!(compare_license_types(SomeSoftware, OtherSoftware)); } #[test] fn compare_license_information_backwards() { - let some_software = SomeSoftware {}; - let other_software = OtherSoftware {}; - - assert!(compare_license_types(other_software, some_software)); + assert!(compare_license_types(OtherSoftware, SomeSoftware)); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 92e440a..43ccdf8 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -784,9 +784,10 @@ name = "traits4" dir = "15_traits" hint = """ Instead of using concrete types as parameters you can use traits. Try replacing -the '??' with 'impl [what goes here?]' +`???` with `impl [what goes here?]`. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters""" +Related section in The Book: +https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters""" [[exercises]] name = "traits5" diff --git a/solutions/15_traits/traits4.rs b/solutions/15_traits/traits4.rs index 4e18198..78b5a11 100644 --- a/solutions/15_traits/traits4.rs +++ b/solutions/15_traits/traits4.rs @@ -1 +1,34 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +trait Licensed { + fn licensing_info(&self) -> String { + "Default license".to_string() + } +} + +struct SomeSoftware; +struct OtherSoftware; + +impl Licensed for SomeSoftware {} +impl Licensed for OtherSoftware {} + +fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool { + software1.licensing_info() == software2.licensing_info() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compare_license_information() { + assert!(compare_license_types(SomeSoftware, OtherSoftware)); + } + + #[test] + fn compare_license_information_backwards() { + assert!(compare_license_types(OtherSoftware, SomeSoftware)); + } +} -- cgit v1.2.3 From 45cfe86fb05a21dd52d9d72d07e881037803395d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:29:25 +0200 Subject: traits5 solution --- exercises/15_traits/traits5.rs | 28 +++++++++++++++++----------- rustlings-macros/info.toml | 6 +++--- solutions/15_traits/traits5.rs | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 15 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs index 3e62283..5b356ac 100644 --- a/exercises/15_traits/traits5.rs +++ b/exercises/15_traits/traits5.rs @@ -1,7 +1,3 @@ -// Your task is to replace the '??' sections so the code compiles. -// -// Don't change any line other than the marked one. - trait SomeTrait { fn some_function(&self) -> bool { true @@ -14,20 +10,30 @@ trait OtherTrait { } } -struct SomeStruct {} -struct OtherStruct {} - +struct SomeStruct; impl SomeTrait for SomeStruct {} impl OtherTrait for SomeStruct {} + +struct OtherStruct; impl SomeTrait for OtherStruct {} impl OtherTrait for OtherStruct {} -// YOU MAY ONLY CHANGE THE NEXT LINE -fn some_func(item: ??) -> bool { +// TODO: Fix the compiler error by only changing the signature of this function. +fn some_func(item: ???) -> bool { item.some_function() && item.other_function() } fn main() { - some_func(SomeStruct {}); - some_func(OtherStruct {}); + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_some_func() { + assert!(some_func(SomeStruct)); + assert!(some_func(OtherStruct)); + } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 43ccdf8..92c878f 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -792,12 +792,12 @@ https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters""" [[exercises]] name = "traits5" dir = "15_traits" -test = false hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try -replacing the '??' with 'impl [what goes here?] + [what goes here?]'. +replacing `???` with 'impl [what goes here?] + [what goes here?]'. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax""" +Related section in The Book: +https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax""" # QUIZ 3 diff --git a/solutions/15_traits/traits5.rs b/solutions/15_traits/traits5.rs index 4e18198..1fb426a 100644 --- a/solutions/15_traits/traits5.rs +++ b/solutions/15_traits/traits5.rs @@ -1 +1,39 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +trait SomeTrait { + fn some_function(&self) -> bool { + true + } +} + +trait OtherTrait { + fn other_function(&self) -> bool { + true + } +} + +struct SomeStruct; +impl SomeTrait for SomeStruct {} +impl OtherTrait for SomeStruct {} + +struct OtherStruct; +impl SomeTrait for OtherStruct {} +impl OtherTrait for OtherStruct {} + +fn some_func(item: impl SomeTrait + OtherTrait) -> bool { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + item.some_function() && item.other_function() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_some_func() { + assert!(some_func(SomeStruct)); + assert!(some_func(OtherStruct)); + } +} -- cgit v1.2.3 From 64c2de95ca95c1d23dcb416723b33ccdfca9c956 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 13:01:52 +0200 Subject: quiz3 solution --- exercises/quizzes/quiz3.rs | 18 ++++++------ rustlings-macros/info.toml | 12 ++++++-- solutions/quizzes/quiz3.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 13 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/quizzes/quiz3.rs b/exercises/quizzes/quiz3.rs index f3cb1bc..c877c5f 100644 --- a/exercises/quizzes/quiz3.rs +++ b/exercises/quizzes/quiz3.rs @@ -3,26 +3,27 @@ // - Traits // // An imaginary magical school has a new report card generation system written -// in Rust! Currently the system only supports creating report cards where the +// in Rust! Currently, the system only supports creating report cards where the // student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the // school also issues alphabetical grades (A+ -> F-) and needs to be able to // print both types of report card! // -// Make the necessary code changes in the struct ReportCard and the impl block -// to support alphabetical report cards. Change the Grade in the second test to -// "A+" to show that your changes allow alphabetical grades. +// Make the necessary code changes in the struct `ReportCard` and the impl +// block to support alphabetical report cards in addition to numerical ones. +// TODO: Adjust the struct as described above. struct ReportCard { grade: f32, student_name: String, student_age: u8, } +// TODO: Adjust the impl block as described above. impl ReportCard { fn print(&self) -> String { format!( "{} ({}) - achieved a grade of {}", - &self.student_name, &self.student_age, &self.grade + &self.student_name, &self.student_age, &self.grade, ) } } @@ -44,21 +45,20 @@ mod tests { }; assert_eq!( report_card.print(), - "Tom Wriggle (12) - achieved a grade of 2.1" + "Tom Wriggle (12) - achieved a grade of 2.1", ); } #[test] fn generate_alphabetic_report_card() { - // TODO: Make sure to change the grade here after you finish the exercise. let report_card = ReportCard { - grade: 2.1, + grade: "A+", student_name: "Gary Plotter".to_string(), student_age: 11, }; assert_eq!( report_card.print(), - "Gary Plotter (11) - achieved a grade of A+" + "Gary Plotter (11) - achieved a grade of A+", ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 92c878f..bed2eaf 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -805,10 +805,16 @@ https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bou name = "quiz3" dir = "quizzes" hint = """ -To find the best solution to this challenge you're going to need to think back -to your knowledge of traits, specifically 'Trait Bound Syntax' +To find the best solution to this challenge, you need to recall your knowledge +of traits, specifically "Trait Bound Syntax": +https://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax -You may also need this: `use std::fmt::Display;`.""" +Here is how to specify a trait bound for an implementation block: +`impl for Foo { … }` + +You may need this: +`use std::fmt::Display;` +""" # LIFETIMES diff --git a/solutions/quizzes/quiz3.rs b/solutions/quizzes/quiz3.rs index 4e18198..e3413fd 100644 --- a/solutions/quizzes/quiz3.rs +++ b/solutions/quizzes/quiz3.rs @@ -1 +1,69 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// This quiz tests: +// - Generics +// - Traits +// +// An imaginary magical school has a new report card generation system written +// in Rust! Currently, the system only supports creating report cards where the +// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the +// school also issues alphabetical grades (A+ -> F-) and needs to be able to +// print both types of report card! +// +// Make the necessary code changes in the struct `ReportCard` and the impl +// block to support alphabetical report cards in addition to numerical ones. + +use std::fmt::Display; + +// Make the struct generic over `T`. +struct ReportCard { + // ^^^ + grade: T, + // ^ + student_name: String, + student_age: u8, +} + +// To be able to print the grade, it has to implement the `Display` trait. +impl ReportCard { + // ^^^^^^^ require that `T` implements `Display`. + fn print(&self) -> String { + format!( + "{} ({}) - achieved a grade of {}", + &self.student_name, &self.student_age, &self.grade, + ) + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn generate_numeric_report_card() { + let report_card = ReportCard { + grade: 2.1, + student_name: "Tom Wriggle".to_string(), + student_age: 12, + }; + assert_eq!( + report_card.print(), + "Tom Wriggle (12) - achieved a grade of 2.1", + ); + } + + #[test] + fn generate_alphabetic_report_card() { + let report_card = ReportCard { + grade: "A+", + student_name: "Gary Plotter".to_string(), + student_age: 11, + }; + assert_eq!( + report_card.print(), + "Gary Plotter (11) - achieved a grade of A+", + ); + } +} -- cgit v1.2.3 From 7efccc36b4c26c444eab2531b6139190af569d6f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 13:24:21 +0200 Subject: lifetimes1 solution --- exercises/16_lifetimes/lifetimes1.rs | 16 ++++++++++++---- rustlings-macros/info.toml | 3 +-- solutions/16_lifetimes/lifetimes1.rs | 29 ++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 7 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/16_lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs index d34f3ab..19e2d39 100644 --- a/exercises/16_lifetimes/lifetimes1.rs +++ b/exercises/16_lifetimes/lifetimes1.rs @@ -3,6 +3,7 @@ // going out of scope before it is used. Remember, references are borrows and do // not own their own data. What if their owner goes out of scope? +// TODO: Fix the compiler error by updating the function signature. fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x @@ -12,9 +13,16 @@ fn longest(x: &str, y: &str) -> &str { } fn main() { - let string1 = String::from("abcd"); - let string2 = "xyz"; + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; - let result = longest(string1.as_str(), string2); - println!("The longest string is '{}'", result); + #[test] + fn test_longest() { + assert_eq!(longest("abcd", "123"), "abcd"); + assert_eq!(longest("abc", "1234"), "1234"); + } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index bed2eaf..e2ebfa5 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -821,9 +821,8 @@ You may need this: [[exercises]] name = "lifetimes1" dir = "16_lifetimes" -test = false hint = """ -Let the compiler guide you. Also take a look at the book if you need help: +Let the compiler guide you. Also take a look at The Book if you need help: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" [[exercises]] diff --git a/solutions/16_lifetimes/lifetimes1.rs b/solutions/16_lifetimes/lifetimes1.rs index 4e18198..ca7b688 100644 --- a/solutions/16_lifetimes/lifetimes1.rs +++ b/solutions/16_lifetimes/lifetimes1.rs @@ -1 +1,28 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// The Rust compiler needs to know how to check whether supplied references are +// valid, so that it can let the programmer know if a reference is at risk of +// going out of scope before it is used. Remember, references are borrows and do +// not own their own data. What if their owner goes out of scope? + +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + // ^^^^ ^^ ^^ ^^ + if x.len() > y.len() { + x + } else { + y + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_longest() { + assert_eq!(longest("abcd", "123"), "abcd"); + assert_eq!(longest("abc", "1234"), "1234"); + } +} -- cgit v1.2.3 From 61872166067ab83fbad87e55c562e28d98368bff Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 16:15:53 +0200 Subject: lifetimes3 solution --- exercises/16_lifetimes/lifetimes3.rs | 7 +++---- rustlings-macros/info.toml | 4 +--- solutions/16_lifetimes/lifetimes3.rs | 19 ++++++++++++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/16_lifetimes/lifetimes3.rs b/exercises/16_lifetimes/lifetimes3.rs index 9b631ca..1cc2759 100644 --- a/exercises/16_lifetimes/lifetimes3.rs +++ b/exercises/16_lifetimes/lifetimes3.rs @@ -1,16 +1,15 @@ // Lifetimes are also needed when structs hold references. +// TODO: Fix the compiler errors about the struct. struct Book { author: &str, title: &str, } fn main() { - let name = String::from("Jill Smith"); - let title = String::from("Fish Flying"); let book = Book { - author: &name, - title: &title, + author: "George Orwell", + title: "1984", }; println!("{} by {}", book.title, book.author); diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index e2ebfa5..f0e34a5 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -843,9 +843,7 @@ inner block: name = "lifetimes3" dir = "16_lifetimes" test = false -hint = """ -If you use a lifetime annotation in a struct's fields, where else does it need -to be added?""" +hint = """Let the compiler guide you :)""" # TESTS diff --git a/solutions/16_lifetimes/lifetimes3.rs b/solutions/16_lifetimes/lifetimes3.rs index 4e18198..16a5a68 100644 --- a/solutions/16_lifetimes/lifetimes3.rs +++ b/solutions/16_lifetimes/lifetimes3.rs @@ -1 +1,18 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// Lifetimes are also needed when structs hold references. + +struct Book<'a> { + // ^^^^ added a lifetime annotation + author: &'a str, + // ^^ + title: &'a str, + // ^^ +} + +fn main() { + let book = Book { + author: "George Orwell", + title: "1984", + }; + + println!("{} by {}", book.title, book.author); +} -- cgit v1.2.3 From a4f8826301c793180d94e891603fab22e9492f5c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 16:29:03 +0200 Subject: tests1 solution --- exercises/17_tests/tests1.rs | 15 ++++++++++----- rustlings-macros/info.toml | 3 --- solutions/17_tests/tests1.rs | 25 ++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 9 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/17_tests/tests1.rs b/exercises/17_tests/tests1.rs index 854a135..7529f9f 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -1,9 +1,9 @@ // Tests are important to ensure that your code does what you think it should -// do. Tests can be run on this file with the following command: rustlings run -// tests1 -// -// This test has a problem with it -- make the test compile! Make the test pass! -// Make the test fail! +// do. + +fn is_even(n: i64) -> bool { + n % 2 == 0 +} fn main() { // You can optionally experiment here. @@ -11,8 +11,13 @@ fn main() { #[cfg(test)] mod tests { + // TODO: Import `is_even`. You can use a wildcard to import everything in + // the outer module. + #[test] fn you_can_assert() { + // TODO: Test the function `is_even` with some values. + assert!(); assert!(); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index f0e34a5..27ed6b9 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -851,9 +851,6 @@ hint = """Let the compiler guide you :)""" name = "tests1" dir = "17_tests" hint = """ -You don't even need to write any code to test -- you can just test values and -run that, even though you wouldn't do that in real life. :) - `assert!` is a macro that needs an argument. Depending on the value of the argument, `assert!` will do nothing (in which case the test will pass) or `assert!` will panic (in which case the test will fail). diff --git a/solutions/17_tests/tests1.rs b/solutions/17_tests/tests1.rs index 4e18198..c52b8b1 100644 --- a/solutions/17_tests/tests1.rs +++ b/solutions/17_tests/tests1.rs @@ -1 +1,24 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// Tests are important to ensure that your code does what you think it should +// do. + +fn is_even(n: i64) -> bool { + n % 2 == 0 +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + // When writing unit tests, it is common to import everything from the outer + // module (`super`) using a wildcard. + use super::*; + + #[test] + fn you_can_assert() { + assert!(is_even(0)); + assert!(!is_even(-1)); + // ^ You can assert `false` using the negation operator `!`. + } +} -- cgit v1.2.3 From 803e32dad2395d309b74b9fde6b9e08577cf8a0a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 16:40:26 +0200 Subject: tests2 solution --- exercises/17_tests/tests2.rs | 13 +++++++++++-- rustlings-macros/info.toml | 6 +----- solutions/17_tests/tests2.rs | 23 ++++++++++++++++++++++- 3 files changed, 34 insertions(+), 8 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index f0899e1..0c6573e 100644 --- a/exercises/17_tests/tests2.rs +++ b/exercises/17_tests/tests2.rs @@ -1,5 +1,8 @@ -// This test has a problem with it -- make the test compile! Make the test pass! -// Make the test fail! +// Calculates the power of 2 using a bit shift. +// `1 << n` is equivalent to "2 to the power of n". +fn power_of_2(n: u8) -> u64 { + 1 << n +} fn main() { // You can optionally experiment here. @@ -7,8 +10,14 @@ fn main() { #[cfg(test)] mod tests { + use super::*; + #[test] fn you_can_assert_eq() { + // TODO: Test the function `power_of_2` with some values. + assert_eq!(); + assert_eq!(); + assert_eq!(); assert_eq!(); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 27ed6b9..4fd2bd8 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -862,13 +862,9 @@ ones pass, and which ones fail :)""" name = "tests2" dir = "17_tests" hint = """ -Like the previous exercise, you don't need to write any code to get this test -to compile and run. - `assert_eq!` is a macro that takes two arguments and compares them. Try giving it two values that are equal! Try giving it two arguments that are different! -Try giving it two values that are of different types! Try switching which -argument comes first and which comes second!""" +Try switching which argument comes first and which comes second!""" [[exercises]] name = "tests3" diff --git a/solutions/17_tests/tests2.rs b/solutions/17_tests/tests2.rs index 4e18198..39a0005 100644 --- a/solutions/17_tests/tests2.rs +++ b/solutions/17_tests/tests2.rs @@ -1 +1,22 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// Calculates the power of 2 using a bit shift. +// `1 << n` is equivalent to "2 to the power of n". +fn power_of_2(n: u8) -> u64 { + 1 << n +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn you_can_assert_eq() { + assert_eq!(power_of_2(0), 1); + assert_eq!(power_of_2(1), 2); + assert_eq!(power_of_2(2), 4); + assert_eq!(power_of_2(3), 8); + } +} -- cgit v1.2.3 From 746cf6863dee8f676596b07e74bad1a19fa2579e Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 17:29:33 +0200 Subject: Remove tests3 and add solution to tests4 --- dev/Cargo.toml | 2 -- exercises/17_tests/tests3.rs | 41 ++++++++++++++++++++++++++++++--------- exercises/17_tests/tests4.rs | 45 ------------------------------------------- rustlings-macros/info.toml | 19 ++++++------------ solutions/17_tests/tests3.rs | 46 +++++++++++++++++++++++++++++++++++++++++++- solutions/17_tests/tests4.rs | 1 - 6 files changed, 83 insertions(+), 71 deletions(-) delete mode 100644 exercises/17_tests/tests4.rs delete mode 100644 solutions/17_tests/tests4.rs (limited to 'rustlings-macros') diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 2c5eaf0..7f3acb5 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -140,8 +140,6 @@ bin = [ { name = "tests2_sol", path = "../solutions/17_tests/tests2.rs" }, { name = "tests3", path = "../exercises/17_tests/tests3.rs" }, { name = "tests3_sol", path = "../solutions/17_tests/tests3.rs" }, - { name = "tests4", path = "../exercises/17_tests/tests4.rs" }, - { name = "tests4_sol", path = "../solutions/17_tests/tests4.rs" }, { name = "iterators1", path = "../exercises/18_iterators/iterators1.rs" }, { name = "iterators1_sol", path = "../solutions/18_iterators/iterators1.rs" }, { name = "iterators2", path = "../exercises/18_iterators/iterators2.rs" }, diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index d1cb489..9fc9318 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -1,9 +1,19 @@ -// This test isn't testing our function -- make it do that in such a way that -// the test passes. Then write a second test that tests whether we get the -// result we expect to get when we call `is_even(5)`. +struct Rectangle { + width: i32, + height: i32, +} + +impl Rectangle { + // Don't change this function. + fn new(width: i32, height: i32) -> Self { + if width <= 0 || height <= 0 { + // Returning a `Result` would be better here. But we want to learn + // how to test functions that can panic. + panic!("Rectangle width and height can't be negative"); + } -fn is_even(num: i32) -> bool { - num % 2 == 0 + Rectangle { width, height } + } } fn main() { @@ -15,12 +25,25 @@ mod tests { use super::*; #[test] - fn is_true_when_even() { - assert!(); + fn correct_width_and_height() { + // TODO: This test should check if the rectangle has the size that we + // pass to its constructor. + let rect = Rectangle::new(10, 20); + assert_eq!(???, 10); // Check width + assert_eq!(???, 20); // Check height + } + + // TODO: This test should check if the program panics when we try to create + // a rectangle with negative width. + #[test] + fn negative_width() { + let _rect = Rectangle::new(-10, 10); } + // TODO: This test should check if the program panics when we try to create + // a rectangle with negative height. #[test] - fn is_false_when_odd() { - assert!(); + fn negative_height() { + let _rect = Rectangle::new(10, -10); } } diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs deleted file mode 100644 index 4303ed0..0000000 --- a/exercises/17_tests/tests4.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Make sure that we're testing for the correct conditions! - -struct Rectangle { - width: i32, - height: i32, -} - -impl Rectangle { - // Only change the test functions themselves - fn new(width: i32, height: i32) -> Self { - if width <= 0 || height <= 0 { - panic!("Rectangle width and height cannot be negative!") - } - Rectangle { width, height } - } -} - -fn main() { - // You can optionally experiment here. -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn correct_width_and_height() { - // This test should check if the rectangle is the size that we pass into its constructor - let rect = Rectangle::new(10, 20); - assert_eq!(???, 10); // check width - assert_eq!(???, 20); // check height - } - - #[test] - fn negative_width() { - // This test should check if program panics when we try to create rectangle with negative width - let _rect = Rectangle::new(-10, 10); - } - - #[test] - fn negative_height() { - // This test should check if program panics when we try to create rectangle with negative height - let _rect = Rectangle::new(10, -10); - } -} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 4fd2bd8..5c24cd3 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -856,7 +856,10 @@ argument, `assert!` will do nothing (in which case the test will pass) or `assert!` will panic (in which case the test will fail). So try giving different values to `assert!` and see which ones compile, which -ones pass, and which ones fail :)""" +ones pass, and which ones fail :) + +If you want to check for `false`, you can negate the result of what you're +checking using `!`, like `assert!(!…)`.""" [[exercises]] name = "tests2" @@ -870,19 +873,9 @@ Try switching which argument comes first and which comes second!""" name = "tests3" dir = "17_tests" hint = """ -You can call a function right where you're passing arguments to `assert!`. So -you could do something like `assert!(having_fun())`. - -If you want to check that you indeed get `false`, you can negate the result of -what you're doing using `!`, like `assert!(!having_fun())`.""" - -[[exercises]] -name = "tests4" -dir = "17_tests" -hint = """ -We expect method `Rectangle::new()` to panic for negative values. +We expect the method `Rectangle::new` to panic for negative values. -To handle that you need to add a special attribute to the test function. +To handle that, you need to add a special attribute to the test function. You can refer to the docs: https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" diff --git a/solutions/17_tests/tests3.rs b/solutions/17_tests/tests3.rs index 4e18198..503f9bc 100644 --- a/solutions/17_tests/tests3.rs +++ b/solutions/17_tests/tests3.rs @@ -1 +1,45 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +struct Rectangle { + width: i32, + height: i32, +} + +impl Rectangle { + // Don't change this function. + fn new(width: i32, height: i32) -> Self { + if width <= 0 || height <= 0 { + // Returning a `Result` would be better here. But we want to learn + // how to test functions that can panic. + panic!("Rectangle width and height can't be negative"); + } + + Rectangle { width, height } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn correct_width_and_height() { + let rect = Rectangle::new(10, 20); + assert_eq!(rect.width, 10); // Check width + assert_eq!(rect.height, 20); // Check height + } + + #[test] + #[should_panic] // Added this attribute to check that the test panics. + fn negative_width() { + let _rect = Rectangle::new(-10, 10); + } + + #[test] + #[should_panic] // Added this attribute to check that the test panics. + fn negative_height() { + let _rect = Rectangle::new(10, -10); + } +} diff --git a/solutions/17_tests/tests4.rs b/solutions/17_tests/tests4.rs deleted file mode 100644 index 4e18198..0000000 --- a/solutions/17_tests/tests4.rs +++ /dev/null @@ -1 +0,0 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 -- cgit v1.2.3 From cf9041c0e42120199e09e74e65c52d69c00db19c Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 02:07:56 +0200 Subject: iterators1 solution --- exercises/18_iterators/iterators1.rs | 21 +++++++++------------ rustlings-macros/info.toml | 15 +-------------- solutions/18_iterators/iterators1.rs | 27 ++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 27 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs index 52b704d..86278a4 100644 --- a/exercises/18_iterators/iterators1.rs +++ b/exercises/18_iterators/iterators1.rs @@ -1,8 +1,6 @@ // When performing operations on elements within a collection, iterators are // essential. This module helps you get familiar with the structure of using an // iterator and how to go through elements within an iterable collection. -// -// Make me compile by filling in the `???`s fn main() { // You can optionally experiment here. @@ -10,19 +8,18 @@ fn main() { #[cfg(test)] mod tests { - use super::*; - #[test] fn iterators() { - let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; + let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; - let mut my_iterable_fav_fruits = ???; // TODO: Step 1 + // TODO: Create an iterator over the array. + let mut fav_fruits_iterator = ???; - assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 2 - assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 3 - assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 4 + assert_eq!(fav_fruits_iterator.next(), Some(&"banana")); + assert_eq!(fav_fruits_iterator.next(), ???); // TODO: Replace `???` + assert_eq!(fav_fruits_iterator.next(), Some(&"avocado")); + assert_eq!(fav_fruits_iterator.next(), ???); // TODO: Replace `???` + assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); + assert_eq!(fav_fruits_iterator.next(), ???); // TODO: Replace `???` } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5c24cd3..5e93986 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -886,22 +886,9 @@ https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-pa name = "iterators1" dir = "18_iterators" hint = """ -Step 1: - -We need to apply something to the collection `my_fav_fruits` before we start to -go through it. What could that be? Take a look at the struct definition for a -vector for inspiration: -https://doc.rust-lang.org/std/vec/struct.Vec.html - -Step 2 & step 3: - -Very similar to the lines above and below. You've got this! - -Step 4: - An iterator goes through all elements in a collection, but what if we've run out of elements? What should we expect here? If you're stuck, take a look at -https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas.""" +https://doc.rust-lang.org/std/iter/trait.Iterator.html""" [[exercises]] name = "iterators2" diff --git a/solutions/18_iterators/iterators1.rs b/solutions/18_iterators/iterators1.rs index 4e18198..93a6008 100644 --- a/solutions/18_iterators/iterators1.rs +++ b/solutions/18_iterators/iterators1.rs @@ -1 +1,26 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// When performing operations on elements within a collection, iterators are +// essential. This module helps you get familiar with the structure of using an +// iterator and how to go through elements within an iterable collection. + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn iterators() { + let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; + + // Create an iterator over the array. + let mut fav_fruits_iterator = my_fav_fruits.iter(); + + assert_eq!(fav_fruits_iterator.next(), Some(&"banana")); + assert_eq!(fav_fruits_iterator.next(), Some(&"custard apple")); + assert_eq!(fav_fruits_iterator.next(), Some(&"avocado")); + assert_eq!(fav_fruits_iterator.next(), Some(&"peach")); + assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); + assert_eq!(fav_fruits_iterator.next(), None); + // ^^^^ reached the end + } +} -- cgit v1.2.3 From eddbb97934b8d358b4fd20cc3063cf4872e39567 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 02:48:21 +0200 Subject: iterators2 solution --- exercises/18_iterators/iterators2.rs | 23 +++++++-------- rustlings-macros/info.toml | 9 ++++-- solutions/18_iterators/iterators2.rs | 57 +++++++++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 17 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs index 8d8909b..5903e65 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -1,31 +1,28 @@ // In this exercise, you'll learn some of the unique advantages that iterators -// can offer. Follow the steps to complete the exercise. +// can offer. -// Step 1. -// Complete the `capitalize_first` function. +// TODO: Complete the `capitalize_first` function. // "hello" -> "Hello" fn capitalize_first(input: &str) -> String { - let mut c = input.chars(); - match c.next() { + let mut chars = input.chars(); + match chars.next() { None => String::new(), - Some(first) => ???, + Some(first) => todo!(), } } -// Step 2. -// Apply the `capitalize_first` function to a slice of string slices. +// TODO: Apply the `capitalize_first` function to a slice of string slices. // Return a vector of strings. // ["hello", "world"] -> ["Hello", "World"] fn capitalize_words_vector(words: &[&str]) -> Vec { - vec![] + // ??? } -// Step 3. -// Apply the `capitalize_first` function again to a slice of string slices. -// Return a single string. +// TODO: Apply the `capitalize_first` function again to a slice of string +// slices. Return a single string. // ["hello", " ", "world"] -> "Hello World" fn capitalize_words_string(words: &[&str]) -> String { - String::new() + // ??? } fn main() { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5e93986..5a33788 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -894,7 +894,7 @@ https://doc.rust-lang.org/std/iter/trait.Iterator.html""" name = "iterators2" dir = "18_iterators" hint = """ -Step 1: +`capitalize_first`: The variable `first` is a `char`. It needs to be capitalized and added to the remaining characters in `c` in order to return the correct `String`. @@ -905,12 +905,15 @@ The remaining characters in `c` can be viewed as a string slice using the The documentation for `char` contains many useful methods. https://doc.rust-lang.org/std/primitive.char.html -Step 2: +Use `char::to_uppercase`. It returns an iterator that can be converted to a +`String`. + +`capitalize_words_vector`: Create an iterator from the slice. Transform the iterated values by applying the `capitalize_first` function. Remember to `collect` the iterator. -Step 3: +`capitalize_words_string`: This is surprisingly similar to the previous solution. `collect` is very powerful and very general. Rust just needs to know the desired type.""" diff --git a/solutions/18_iterators/iterators2.rs b/solutions/18_iterators/iterators2.rs index 4e18198..db05f29 100644 --- a/solutions/18_iterators/iterators2.rs +++ b/solutions/18_iterators/iterators2.rs @@ -1 +1,56 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// In this exercise, you'll learn some of the unique advantages that iterators +// can offer. + +// "hello" -> "Hello" +fn capitalize_first(input: &str) -> String { + let mut chars = input.chars(); + match chars.next() { + None => String::new(), + Some(first) => first.to_uppercase().to_string() + chars.as_str(), + } +} + +// Apply the `capitalize_first` function to a slice of string slices. +// Return a vector of strings. +// ["hello", "world"] -> ["Hello", "World"] +fn capitalize_words_vector(words: &[&str]) -> Vec { + words.iter().map(|word| capitalize_first(word)).collect() +} + +// Apply the `capitalize_first` function again to a slice of string +// slices. Return a single string. +// ["hello", " ", "world"] -> "Hello World" +fn capitalize_words_string(words: &[&str]) -> String { + words.iter().map(|word| capitalize_first(word)).collect() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_success() { + assert_eq!(capitalize_first("hello"), "Hello"); + } + + #[test] + fn test_empty() { + assert_eq!(capitalize_first(""), ""); + } + + #[test] + fn test_iterate_string_vec() { + let words = vec!["hello", "world"]; + assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]); + } + + #[test] + fn test_iterate_into_string() { + let words = vec!["hello", " ", "world"]; + assert_eq!(capitalize_words_string(&words), "Hello World"); + } +} -- cgit v1.2.3 From 56a9197f55356a0a6503d6fa6cb2241d676bd051 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 15:00:13 +0200 Subject: iterators3 solution --- exercises/18_iterators/iterators3.rs | 55 ++++++++------------------- rustlings-macros/info.toml | 6 +-- solutions/18_iterators/iterators3.rs | 74 +++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 43 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index dfe4014..b5d05f6 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -1,40 +1,26 @@ -// This is a bigger exercise than most of the others! You can do it! Here is -// your mission, should you choose to accept it: -// 1. Complete the divide function to get the first four tests to pass. -// 2. Get the remaining tests to pass by completing the result_with_list and -// list_of_results functions. - #[derive(Debug, PartialEq, Eq)] enum DivisionError { - NotDivisible(NotDivisibleError), DivideByZero, + NotDivisible, } -#[derive(Debug, PartialEq, Eq)] -struct NotDivisibleError { - dividend: i32, - divisor: i32, -} - -// Calculate `a` divided by `b` if `a` is evenly divisible by `b`. +// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`. // Otherwise, return a suitable error. fn divide(a: i32, b: i32) -> Result { todo!(); } -// Complete the function and return a value of the correct type so the test -// passes. -// Desired output: Ok([1, 11, 1426, 3]) -fn result_with_list() -> () { - let numbers = vec![27, 297, 38502, 81]; +// TODO: Add the correct return type and complete the function body. +// Desired output: `Ok([1, 11, 1426, 3])` +fn result_with_list() { + let numbers = [27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); } -// Complete the function and return a value of the correct type so the test -// passes. -// Desired output: [Ok(1), Ok(11), Ok(1426), Ok(3)] -fn list_of_results() -> () { - let numbers = vec![27, 297, 38502, 81]; +// TODO: Add the correct return type and complete the function body. +// Desired output: `[Ok(1), Ok(11), Ok(1426), Ok(3)]` +fn list_of_results() { + let numbers = [27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); } @@ -52,19 +38,13 @@ mod tests { } #[test] - fn test_not_divisible() { - assert_eq!( - divide(81, 6), - Err(DivisionError::NotDivisible(NotDivisibleError { - dividend: 81, - divisor: 6 - })) - ); + fn test_divide_by_0() { + assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); } #[test] - fn test_divide_by_0() { - assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); + fn test_not_divisible() { + assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); } #[test] @@ -74,14 +54,11 @@ mod tests { #[test] fn test_result_with_list() { - assert_eq!(format!("{:?}", result_with_list()), "Ok([1, 11, 1426, 3])"); + assert_eq!(result_with_list().unwarp(), [1, 11, 1426, 3]); } #[test] fn test_list_of_results() { - assert_eq!( - format!("{:?}", list_of_results()), - "[Ok(1), Ok(11), Ok(1426), Ok(3)]" - ); + assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5a33788..8b1feb4 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -922,8 +922,8 @@ powerful and very general. Rust just needs to know the desired type.""" name = "iterators3" dir = "18_iterators" hint = """ -The `divide` function needs to return the correct error when even division is -not possible. +The `divide` function needs to return the correct error when the divisor is 0 or +when even division is not possible. The `division_results` variable needs to be collected into a collection type. @@ -934,7 +934,7 @@ The `list_of_results` function needs to return a vector of results. See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how the `FromIterator` trait is used in `collect()`. This trait is REALLY -powerful! It can make the solution to this exercise infinitely easier.""" +powerful! It can make the solution to this exercise much easier.""" [[exercises]] name = "iterators4" diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs index 4e18198..d66d1ef 100644 --- a/solutions/18_iterators/iterators3.rs +++ b/solutions/18_iterators/iterators3.rs @@ -1 +1,73 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +#[derive(Debug, PartialEq, Eq)] +enum DivisionError { + DivideByZero, + NotDivisible, +} + +fn divide(a: i64, b: i64) -> Result { + if b == 0 { + return Err(DivisionError::DivideByZero); + } + + if a % b != 0 { + return Err(DivisionError::NotDivisible); + } + + Ok(a / b) +} + +fn result_with_list() -> Result, DivisionError> { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let numbers = [27, 297, 38502, 81]; + let division_results = numbers.into_iter().map(|n| divide(n, 27)); + // Collects to the expected return type. Returns the first error in the + // division results (if one exists). + division_results.collect() +} + +fn list_of_results() -> Vec> { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let numbers = [27, 297, 38502, 81]; + let division_results = numbers.into_iter().map(|n| divide(n, 27)); + // Collects to the expected return type. + division_results.collect() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_success() { + assert_eq!(divide(81, 9), Ok(9)); + } + + #[test] + fn test_divide_by_0() { + assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); + } + + #[test] + fn test_not_divisible() { + assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); + } + + #[test] + fn test_divide_0_by_something() { + assert_eq!(divide(0, 81), Ok(0)); + } + + #[test] + fn test_result_with_list() { + assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]); + } + + #[test] + fn test_list_of_results() { + assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]); + } +} -- cgit v1.2.3 From 2af437fd901345f2613217cbf325718672d04100 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 15:31:15 +0200 Subject: iterators4 solution --- exercises/18_iterators/iterators4.rs | 14 +++---- rustlings-macros/info.toml | 4 +- solutions/18_iterators/iterators4.rs | 72 +++++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 10 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index ae4d502..08ba365 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -1,9 +1,9 @@ -fn factorial(num: u64) -> u64 { - // Complete this function to return the factorial of num +fn factorial(num: u8) -> u64 { + // TODO: Complete this function to return the factorial of `num`. // Do not use: // - early returns (using the `return` keyword explicitly) // Try not to use: - // - imperative style loops (for, while) + // - imperative style loops (for/while) // - additional variables // For an extra challenge, don't use: // - recursion @@ -19,20 +19,20 @@ mod tests { #[test] fn factorial_of_0() { - assert_eq!(1, factorial(0)); + assert_eq!(factorial(0), 1); } #[test] fn factorial_of_1() { - assert_eq!(1, factorial(1)); + assert_eq!(factorial(1), 1); } #[test] fn factorial_of_2() { - assert_eq!(2, factorial(2)); + assert_eq!(factorial(2), 2); } #[test] fn factorial_of_4() { - assert_eq!(24, factorial(4)); + assert_eq!(factorial(4), 24); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 8b1feb4..72f956b 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -942,10 +942,10 @@ dir = "18_iterators" hint = """ In an imperative language, you might write a `for` loop that updates a mutable variable. Or, you might write code utilizing recursion and a match clause. In -Rust you can take another functional approach, computing the factorial +Rust, you can take another functional approach, computing the factorial elegantly with ranges and iterators. -Hint 2: Check out the `fold` and `rfold` methods!""" +Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" diff --git a/solutions/18_iterators/iterators4.rs b/solutions/18_iterators/iterators4.rs index 4e18198..4c3c49d 100644 --- a/solutions/18_iterators/iterators4.rs +++ b/solutions/18_iterators/iterators4.rs @@ -1 +1,71 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// 3 possible solutions are presented. + +// With `for` loop and a mutable variable. +fn factorial_for(num: u64) -> u64 { + let mut result = 1; + + for x in 2..=num { + result *= x; + } + + result +} + +// Equivalent to `factorial_for` but shorter and without a `for` loop and +// mutable variables. +fn factorial_fold(num: u64) -> u64 { + // Case num==0: The iterator 2..=0 is empty + // -> The initial value of `fold` is returned which is 1. + // Case num==1: The iterator 2..=1 is also empty + // -> The initial value 1 is returned. + // Case num==2: The iterator 2..=2 contains one element + // -> The initial value 1 is multiplied by 2 and the result + // is returned. + // Case num==3: The iterator 2..=3 contains 2 elements + // -> 1 * 2 is calculated, then the result 2 is multiplied by + // the second element 3 so the result 6 is returned. + // And so on… + (2..=num).fold(1, |acc, x| acc * x) +} + +// Equivalent to `factorial_fold` but with a built-in method that is suggested +// by Clippy. +fn factorial_product(num: u64) -> u64 { + (2..=num).product() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn factorial_of_0() { + assert_eq!(factorial_for(0), 1); + assert_eq!(factorial_fold(0), 1); + assert_eq!(factorial_product(0), 1); + } + + #[test] + fn factorial_of_1() { + assert_eq!(factorial_for(1), 1); + assert_eq!(factorial_fold(1), 1); + assert_eq!(factorial_product(1), 1); + } + #[test] + fn factorial_of_2() { + assert_eq!(factorial_for(2), 2); + assert_eq!(factorial_fold(2), 2); + assert_eq!(factorial_product(2), 2); + } + + #[test] + fn factorial_of_4() { + assert_eq!(factorial_for(4), 24); + assert_eq!(factorial_fold(4), 24); + assert_eq!(factorial_product(4), 24); + } +} -- cgit v1.2.3 From 61c7eaed6251fb8a28b00ea97b22d1f1b778a72b Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 21:24:35 +0200 Subject: box1 solution --- exercises/19_smart_pointers/box1.rs | 30 +++++++++++------------ rustlings-macros/info.toml | 15 ++++-------- solutions/19_smart_pointers/box1.rs | 48 ++++++++++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 27 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/19_smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs index c8c2640..d70e1c3 100644 --- a/exercises/19_smart_pointers/box1.rs +++ b/exercises/19_smart_pointers/box1.rs @@ -4,45 +4,43 @@ // `Box` - a smart pointer used to store data on the heap, which also allows us // to wrap a recursive type. // -// The recursive type we're implementing in this exercise is the `cons list` - a +// The recursive type we're implementing in this exercise is the "cons list", a // data structure frequently found in functional programming languages. Each -// item in a cons list contains two elements: the value of the current item and +// item in a cons list contains two elements: The value of the current item and // the next item. The last item is a value called `Nil`. -// -// Step 1: use a `Box` in the enum definition to make the code compile -// Step 2: create both empty and non-empty cons lists by replacing `todo!()` -// -// Note: the tests should not be changed +// TODO: Use a `Box` in the enum definition to make the code compile. #[derive(PartialEq, Debug)] enum List { Cons(i32, List), Nil, } -fn main() { - println!("This is an empty cons list: {:?}", create_empty_list()); - println!( - "This is a non-empty cons list: {:?}", - create_non_empty_list() - ); -} - +// TODO: Create an empty cons list. fn create_empty_list() -> List { todo!() } +// TODO: Create a non-empty cons list. fn create_non_empty_list() -> List { todo!() } +fn main() { + println!("This is an empty cons list: {:?}", create_empty_list()); + println!( + "This is a non-empty cons list: {:?}", + create_non_empty_list(), + ); +} + #[cfg(test)] mod tests { use super::*; #[test] fn test_create_empty_list() { - assert_eq!(List::Nil, create_empty_list()); + assert_eq!(create_empty_list(), List::Nil); } #[test] diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 72f956b..744ad08 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -969,21 +969,16 @@ a different method that could make your code more compact than using `fold`.""" name = "box1" dir = "19_smart_pointers" hint = """ -Step 1: - -The compiler's message should help: since we cannot store the value of the +The compiler's message should help: Since we cannot store the value of the actual type when working with recursive types, we need to store a reference (pointer) to its value. -We should, therefore, place our `List` inside a `Box`. More details in the book -here: https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes - -Step 2: +We should, therefore, place our `List` inside a `Box`. More details in The Book: +https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes -Creating an empty list should be fairly straightforward (hint: peek at the -assertions). +Creating an empty list should be fairly straightforward (Hint: Read the tests). -For a non-empty list keep in mind that we want to use our `Cons` "list builder". +For a non-empty list, keep in mind that we want to use our `Cons` list builder. Although the current list is one of integers (`i32`), feel free to change the definition and try other types!""" diff --git a/solutions/19_smart_pointers/box1.rs b/solutions/19_smart_pointers/box1.rs index 4e18198..189cc56 100644 --- a/solutions/19_smart_pointers/box1.rs +++ b/solutions/19_smart_pointers/box1.rs @@ -1 +1,47 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// At compile time, Rust needs to know how much space a type takes up. This +// becomes problematic for recursive types, where a value can have as part of +// itself another value of the same type. To get around the issue, we can use a +// `Box` - a smart pointer used to store data on the heap, which also allows us +// to wrap a recursive type. +// +// The recursive type we're implementing in this exercise is the "cons list", a +// data structure frequently found in functional programming languages. Each +// item in a cons list contains two elements: The value of the current item and +// the next item. The last item is a value called `Nil`. + +#[derive(PartialEq, Debug)] +enum List { + Cons(i32, Box), + Nil, +} + +fn create_empty_list() -> List { + List::Nil +} + +fn create_non_empty_list() -> List { + List::Cons(42, Box::new(List::Nil)) +} + +fn main() { + println!("This is an empty cons list: {:?}", create_empty_list()); + println!( + "This is a non-empty cons list: {:?}", + create_non_empty_list(), + ); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_empty_list() { + assert_eq!(create_empty_list(), List::Nil); + } + + #[test] + fn test_create_non_empty_list() { + assert_ne!(create_empty_list(), create_non_empty_list()); + } +} -- cgit v1.2.3 From f3842aa746aa77a3fdf0f699951cde0d49f042c4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 29 Jun 2024 01:20:59 +0200 Subject: rc1 solution --- exercises/19_smart_pointers/rc1.rs | 21 ++++---- rustlings-macros/info.toml | 4 +- solutions/19_smart_pointers/rc1.rs | 105 ++++++++++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 15 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs index 19de3db..ecd3438 100644 --- a/exercises/19_smart_pointers/rc1.rs +++ b/exercises/19_smart_pointers/rc1.rs @@ -1,15 +1,12 @@ // In this exercise, we want to express the concept of multiple owners via the -// Rc type. This is a model of our solar system - there is a Sun type and -// multiple Planets. The Planets take ownership of the sun, indicating that they -// revolve around the sun. -// -// Make this code compile by using the proper Rc primitives to express that the -// sun has multiple owners. +// `Rc` type. This is a model of our solar system - there is a `Sun` type and +// multiple `Planet`s. The planets take ownership of the sun, indicating that +// they revolve around the sun. use std::rc::Rc; #[derive(Debug)] -struct Sun {} +struct Sun; #[derive(Debug)] enum Planet { @@ -25,7 +22,7 @@ enum Planet { impl Planet { fn details(&self) { - println!("Hi from {:?}!", self) + println!("Hi from {self:?}!"); } } @@ -39,7 +36,7 @@ mod tests { #[test] fn rc1() { - let sun = Rc::new(Sun {}); + let sun = Rc::new(Sun); println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference let mercury = Planet::Mercury(Rc::clone(&sun)); @@ -63,17 +60,17 @@ mod tests { jupiter.details(); // TODO - let saturn = Planet::Saturn(Rc::new(Sun {})); + let saturn = Planet::Saturn(Rc::new(Sun)); println!("reference count = {}", Rc::strong_count(&sun)); // 7 references saturn.details(); // TODO - let uranus = Planet::Uranus(Rc::new(Sun {})); + let uranus = Planet::Uranus(Rc::new(Sun)); println!("reference count = {}", Rc::strong_count(&sun)); // 8 references uranus.details(); // TODO - let neptune = Planet::Neptune(Rc::new(Sun {})); + let neptune = Planet::Neptune(Rc::new(Sun)); println!("reference count = {}", Rc::strong_count(&sun)); // 9 references neptune.details(); diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 744ad08..5b3f781 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -993,11 +993,11 @@ of the `Sun`. After using `drop()` to move the `Planet`s out of scope individually, the reference count goes down. -In the end the `Sun` only has one reference again, to itself. +In the end, the `Sun` only has one reference again, to itself. See more at: https://doc.rust-lang.org/book/ch15-04-rc.html -* Unfortunately Pluto is no longer considered a planet :(""" +Unfortunately, Pluto is no longer considered a planet :(""" [[exercises]] name = "arc1" diff --git a/solutions/19_smart_pointers/rc1.rs b/solutions/19_smart_pointers/rc1.rs index 4e18198..c0a41ab 100644 --- a/solutions/19_smart_pointers/rc1.rs +++ b/solutions/19_smart_pointers/rc1.rs @@ -1 +1,104 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// In this exercise, we want to express the concept of multiple owners via the +// `Rc` type. This is a model of our solar system - there is a `Sun` type and +// multiple `Planet`s. The planets take ownership of the sun, indicating that +// they revolve around the sun. + +use std::rc::Rc; + +#[derive(Debug)] +struct Sun; + +#[derive(Debug)] +enum Planet { + Mercury(Rc), + Venus(Rc), + Earth(Rc), + Mars(Rc), + Jupiter(Rc), + Saturn(Rc), + Uranus(Rc), + Neptune(Rc), +} + +impl Planet { + fn details(&self) { + println!("Hi from {self:?}!"); + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rc1() { + let sun = Rc::new(Sun); + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + + let mercury = Planet::Mercury(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + mercury.details(); + + let venus = Planet::Venus(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + venus.details(); + + let earth = Planet::Earth(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + earth.details(); + + let mars = Planet::Mars(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + mars.details(); + + let jupiter = Planet::Jupiter(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + jupiter.details(); + + let saturn = Planet::Saturn(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + saturn.details(); + + // TODO + let uranus = Planet::Uranus(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + uranus.details(); + + // TODO + let neptune = Planet::Neptune(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 9 references + neptune.details(); + + assert_eq!(Rc::strong_count(&sun), 9); + + drop(neptune); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + + drop(uranus); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + + drop(saturn); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + + drop(jupiter); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + + drop(mars); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + + drop(earth); + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + + drop(venus); + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + + drop(mercury); + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + + assert_eq!(Rc::strong_count(&sun), 1); + } +} -- cgit v1.2.3 From a943f5ba32412cf5b8fdd8665c1082ecab3ec545 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 29 Jun 2024 01:48:00 +0200 Subject: arc1 solution --- exercises/19_smart_pointers/arc1.rs | 55 +++++++++++++++++++------------------ rustlings-macros/info.toml | 2 +- solutions/19_smart_pointers/arc1.rs | 43 ++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 28 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/19_smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs index 7b31fa8..c3d714d 100644 --- a/exercises/19_smart_pointers/arc1.rs +++ b/exercises/19_smart_pointers/arc1.rs @@ -1,39 +1,42 @@ -// In this exercise, we are given a Vec of u32 called "numbers" with values -// ranging from 0 to 99 -- [ 0, 1, 2, ..., 98, 99 ] We would like to use this -// set of numbers within 8 different threads simultaneously. Each thread is -// going to get the sum of every eighth value, with an offset. +// In this exercise, we are given a `Vec` of u32 called `numbers` with values +// ranging from 0 to 99. We would like to use this set of numbers within 8 +// different threads simultaneously. Each thread is going to get the sum of +// every eighth value with an offset. // -// The first thread (offset 0), will sum 0, 8, 16, ... -// The second thread (offset 1), will sum 1, 9, 17, ... -// The third thread (offset 2), will sum 2, 10, 18, ... -// ... -// The eighth thread (offset 7), will sum 7, 15, 23, ... +// The first thread (offset 0), will sum 0, 8, 16, … +// The second thread (offset 1), will sum 1, 9, 17, … +// The third thread (offset 2), will sum 2, 10, 18, … +// … +// The eighth thread (offset 7), will sum 7, 15, 23, … // -// Because we are using threads, our values need to be thread-safe. Therefore, -// we are using Arc. We need to make a change in each of the two TODOs. -// -// Make this code compile by filling in a value for `shared_numbers` where the -// first TODO comment is, and create an initial binding for `child_numbers` -// where the second TODO comment is. Try not to create any copies of the -// `numbers` Vec! +// Because we are using threads, our values need to be thread-safe. Therefore, +// we are using `Arc`. -#![forbid(unused_imports)] // Do not change this, (or the next) line. -use std::sync::Arc; -use std::thread; +// Don't change the lines below. +#![forbid(unused_imports)] +use std::{sync::Arc, thread}; fn main() { let numbers: Vec<_> = (0..100u32).collect(); - let shared_numbers = // TODO - let mut joinhandles = Vec::new(); + + // TODO: Define `shared_numbers` by using `Arc`. + // let shared_numbers = ???; + + let mut join_handles = Vec::new(); for offset in 0..8 { - let child_numbers = // TODO - joinhandles.push(thread::spawn(move || { + // TODO: Define `child_numbers` using `shared_numbers`. + // let child_numbers = ???; + + let handle = thread::spawn(move || { let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); - println!("Sum of offset {} is {}", offset, sum); - })); + println!("Sum of offset {offset} is {sum}"); + }); + + join_handles.push(handle); } - for handle in joinhandles.into_iter() { + + for handle in join_handles.into_iter() { handle.join().unwrap(); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5b3f781..23b6181 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1004,7 +1004,7 @@ name = "arc1" dir = "19_smart_pointers" test = false hint = """ -Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order +Make `shared_numbers` be an `Arc` from the `numbers` vector. Then, in order to avoid creating a copy of `numbers`, you'll need to create `child_numbers` inside the loop but still in the main thread. diff --git a/solutions/19_smart_pointers/arc1.rs b/solutions/19_smart_pointers/arc1.rs index 4e18198..a520dfe 100644 --- a/solutions/19_smart_pointers/arc1.rs +++ b/solutions/19_smart_pointers/arc1.rs @@ -1 +1,42 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// In this exercise, we are given a `Vec` of u32 called `numbers` with values +// ranging from 0 to 99. We would like to use this set of numbers within 8 +// different threads simultaneously. Each thread is going to get the sum of +// every eighth value with an offset. +// +// The first thread (offset 0), will sum 0, 8, 16, … +// The second thread (offset 1), will sum 1, 9, 17, … +// The third thread (offset 2), will sum 2, 10, 18, … +// … +// The eighth thread (offset 7), will sum 7, 15, 23, … +// +// Because we are using threads, our values need to be thread-safe. Therefore, +// we are using `Arc`. + +// Don't change the lines below. +#![forbid(unused_imports)] +use std::{sync::Arc, thread}; + +fn main() { + let numbers: Vec<_> = (0..100u32).collect(); + + let shared_numbers = Arc::new(numbers); + // ^^^^^^^^^^^^^^^^^ + + let mut join_handles = Vec::new(); + + for offset in 0..8 { + let child_numbers = Arc::clone(&shared_numbers); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + let handle = thread::spawn(move || { + let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); + println!("Sum of offset {offset} is {sum}"); + }); + + join_handles.push(handle); + } + + for handle in join_handles.into_iter() { + handle.join().unwrap(); + } +} -- cgit v1.2.3 From 663a03a17b2d2001f4f3f35a59cd2e2aa5f2bb24 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 29 Jun 2024 02:07:56 +0200 Subject: cow1 solution --- exercises/19_smart_pointers/cow1.rs | 78 +++++++++++++++++-------------------- rustlings-macros/info.toml | 6 +-- solutions/19_smart_pointers/cow1.rs | 69 +++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 47 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/19_smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs index 754c0ba..5ecf848 100644 --- a/exercises/19_smart_pointers/cow1.rs +++ b/exercises/19_smart_pointers/cow1.rs @@ -1,24 +1,18 @@ -// This exercise explores the Cow, or Clone-On-Write type. Cow is a -// clone-on-write smart pointer. It can enclose and provide immutable access to -// borrowed data, and clone the data lazily when mutation or ownership is -// required. The type is designed to work with general borrowed data via the -// Borrow trait. -// -// This exercise is meant to show you what to expect when passing data to Cow. -// Fix the unit tests by checking for Cow::Owned(_) and Cow::Borrowed(_) at the -// TODO markers. +// This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can +// enclose and provide immutable access to borrowed data and clone the data +// lazily when mutation or ownership is required. The type is designed to work +// with general borrowed data via the `Borrow` trait. use std::borrow::Cow; -fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { - for i in 0..input.len() { - let v = input[i]; - if v < 0 { +fn abs_all(input: &mut Cow<[i32]>) { + for ind in 0..input.len() { + let value = input[ind]; + if value < 0 { // Clones into a vector if not already owned. - input.to_mut()[i] = -v; + input.to_mut()[ind] = -value; } } - input } fn main() { @@ -30,47 +24,45 @@ mod tests { use super::*; #[test] - fn reference_mutation() -> Result<(), &'static str> { + fn reference_mutation() { // Clone occurs because `input` needs to be mutated. - let slice = [-1, 0, 1]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - Cow::Owned(_) => Ok(()), - _ => Err("Expected owned value"), - } + let vec = vec![-1, 0, 1]; + let mut input = Cow::from(&vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Owned(_))); } #[test] - fn reference_no_mutation() -> Result<(), &'static str> { + fn reference_no_mutation() { // No clone occurs because `input` doesn't need to be mutated. - let slice = [0, 1, 2]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - // TODO - } + let vec = vec![0, 1, 2]; + let mut input = Cow::from(&vec); + abs_all(&mut input); + // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. + assert!(matches!(input, todo!())); } #[test] - fn owned_no_mutation() -> Result<(), &'static str> { - // We can also pass `slice` without `&` so Cow owns it directly. In this - // case no mutation occurs and thus also no clone, but the result is + fn owned_no_mutation() { + // We can also pass `vec` without `&` so `Cow` owns it directly. In this + // case, no mutation occurs and thus also no clone. But the result is // still owned because it was never borrowed or mutated. - let slice = vec![0, 1, 2]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - } + let vec = vec![0, 1, 2]; + let mut input = Cow::from(vec); + abs_all(&mut input); + // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. + assert!(matches!(input, todo!())); } #[test] - fn owned_mutation() -> Result<(), &'static str> { + fn owned_mutation() { // Of course this is also the case if a mutation does occur. In this - // case the call to `to_mut()` in the abs_all() function returns a + // case, the call to `to_mut()` in the `abs_all` function returns a // reference to the same data as before. - let slice = vec![-1, 0, 1]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - } + let vec = vec![-1, 0, 1]; + let mut input = Cow::from(vec); + abs_all(&mut input); + // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. + assert!(matches!(input, todo!())); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 23b6181..cacdad9 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1020,11 +1020,11 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html""" name = "cow1" dir = "19_smart_pointers" hint = """ -If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is +If `Cow` already owns the data, it doesn't need to clone it when `to_mut()` is called. -Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation -on the `Cow` type.""" +Check out the documentation of the `Cow` type: +https://doc.rust-lang.org/std/borrow/enum.Cow.html""" # THREADS diff --git a/solutions/19_smart_pointers/cow1.rs b/solutions/19_smart_pointers/cow1.rs index 4e18198..0a21a91 100644 --- a/solutions/19_smart_pointers/cow1.rs +++ b/solutions/19_smart_pointers/cow1.rs @@ -1 +1,68 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can +// enclose and provide immutable access to borrowed data and clone the data +// lazily when mutation or ownership is required. The type is designed to work +// with general borrowed data via the `Borrow` trait. + +use std::borrow::Cow; + +fn abs_all(input: &mut Cow<[i32]>) { + for ind in 0..input.len() { + let value = input[ind]; + if value < 0 { + // Clones into a vector if not already owned. + input.to_mut()[ind] = -value; + } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reference_mutation() { + // Clone occurs because `input` needs to be mutated. + let vec = vec![-1, 0, 1]; + let mut input = Cow::from(&vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Owned(_))); + } + + #[test] + fn reference_no_mutation() { + // No clone occurs because `input` doesn't need to be mutated. + let vec = vec![0, 1, 2]; + let mut input = Cow::from(&vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Borrowed(_))); + // ^^^^^^^^^^^^^^^^ + } + + #[test] + fn owned_no_mutation() { + // We can also pass `vec` without `&` so `Cow` owns it directly. In this + // case, no mutation occurs and thus also no clone. But the result is + // still owned because it was never borrowed or mutated. + let vec = vec![0, 1, 2]; + let mut input = Cow::from(vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Owned(_))); + // ^^^^^^^^^^^^^ + } + + #[test] + fn owned_mutation() { + // Of course this is also the case if a mutation does occur. In this + // case, the call to `to_mut()` in the `abs_all` function returns a + // reference to the same data as before. + let vec = vec![-1, 0, 1]; + let mut input = Cow::from(vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Owned(_))); + // ^^^^^^^^^^^^^ + } +} -- cgit v1.2.3 From b000164eedaf5ada18ce0562aa9b7aed25663458 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 10:59:33 +0200 Subject: threads1 solution --- exercises/20_threads/threads1.rs | 24 ++++++++++++++---------- rustlings-macros/info.toml | 2 +- solutions/20_threads/threads1.rs | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 12 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/20_threads/threads1.rs b/exercises/20_threads/threads1.rs index bf0b8e0..01f9ff4 100644 --- a/exercises/20_threads/threads1.rs +++ b/exercises/20_threads/threads1.rs @@ -3,31 +3,35 @@ // wait until all the spawned threads have finished and should collect their // return values into a vector. -use std::thread; -use std::time::{Duration, Instant}; +use std::{ + thread, + time::{Duration, Instant}, +}; fn main() { - let mut handles = vec![]; + let mut handles = Vec::new(); for i in 0..10 { - handles.push(thread::spawn(move || { + let handle = thread::spawn(move || { let start = Instant::now(); thread::sleep(Duration::from_millis(250)); - println!("thread {} is complete", i); + println!("Thread {i} done"); start.elapsed().as_millis() - })); + }); + handles.push(handle); } - let mut results: Vec = vec![]; + let mut results = Vec::new(); for handle in handles { - // TODO: a struct is returned from thread::spawn, can you use it? + // TODO: Collect the results of all threads into the `results` vector. + // Use the `JoinHandle` struct which is returned by `thread::spawn`. } if results.len() != 10 { - panic!("Oh no! All the spawned threads did not finish!"); + panic!("Oh no! Some thread isn't done yet!"); } println!(); for (i, result) in results.into_iter().enumerate() { - println!("thread {} took {}ms", i, result); + println!("Thread {i} took {result}ms"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index cacdad9..37afa17 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1037,7 +1037,7 @@ hint = """ https://doc.rust-lang.org/std/thread/fn.spawn.html A challenge with multi-threaded applications is that the main thread can -finish before the spawned threads are completed. +finish before the spawned threads are done. https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles Use the `JoinHandle`s to wait for each thread to finish and collect their diff --git a/solutions/20_threads/threads1.rs b/solutions/20_threads/threads1.rs index 4e18198..7f3dd29 100644 --- a/solutions/20_threads/threads1.rs +++ b/solutions/20_threads/threads1.rs @@ -1 +1,37 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// This program spawns multiple threads that each run for at least 250ms, and +// each thread returns how much time they took to complete. The program should +// wait until all the spawned threads have finished and should collect their +// return values into a vector. + +use std::{ + thread, + time::{Duration, Instant}, +}; + +fn main() { + let mut handles = Vec::new(); + for i in 0..10 { + let handle = thread::spawn(move || { + let start = Instant::now(); + thread::sleep(Duration::from_millis(250)); + println!("Thread {i} done"); + start.elapsed().as_millis() + }); + handles.push(handle); + } + + let mut results = Vec::new(); + for handle in handles { + // Collect the results of all threads into the `results` vector. + results.push(handle.join().unwrap()); + } + + if results.len() != 10 { + panic!("Oh no! Some thread isn't done yet!"); + } + + println!(); + for (i, result) in results.into_iter().enumerate() { + println!("Thread {i} took {result}ms"); + } +} -- cgit v1.2.3 From dfa2b44f718906dfac54816bb582880066c3dff0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:11:11 +0200 Subject: threads2 solution --- exercises/20_threads/threads2.rs | 27 +++++++++++++------------- rustlings-macros/info.toml | 10 +++++----- solutions/20_threads/threads2.rs | 42 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 20 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/20_threads/threads2.rs b/exercises/20_threads/threads2.rs index 2bdeba9..7020cb9 100644 --- a/exercises/20_threads/threads2.rs +++ b/exercises/20_threads/threads2.rs @@ -1,35 +1,34 @@ // Building on the last exercise, we want all of the threads to complete their -// work but this time the spawned threads need to be in charge of updating a -// shared value: JobStatus.jobs_completed +// work. But this time, the spawned threads need to be in charge of updating a +// shared value: `JobStatus.jobs_done` -use std::sync::Arc; -use std::thread; -use std::time::Duration; +use std::{sync::Arc, thread, time::Duration}; struct JobStatus { - jobs_completed: u32, + jobs_done: u32, } fn main() { - // TODO: `Arc` isn't enough if you want a **mutable** shared state - let status = Arc::new(JobStatus { jobs_completed: 0 }); + // TODO: `Arc` isn't enough if you want a **mutable** shared state. + let status = Arc::new(JobStatus { jobs_done: 0 }); - let mut handles = vec![]; + let mut handles = Vec::new(); for _ in 0..10 { let status_shared = Arc::clone(&status); let handle = thread::spawn(move || { thread::sleep(Duration::from_millis(250)); - // TODO: You must take an action before you update a shared value - status_shared.jobs_completed += 1; + + // TODO: You must take an action before you update a shared value. + status_shared.jobs_done += 1; }); handles.push(handle); } - // Waiting for all jobs to complete + // Waiting for all jobs to complete. for handle in handles { handle.join().unwrap(); } - // TODO: Print the value of `JobStatus.jobs_completed` - println!("Jobs completed: {}", ???); + // TODO: Print the value of `JobStatus.jobs_done`. + println!("Jobs done: {}", todo!()); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 37afa17..ab8c121 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1051,19 +1051,19 @@ dir = "20_threads" test = false hint = """ `Arc` is an Atomic Reference Counted pointer that allows safe, shared access -to **immutable** data. But we want to *change* the number of `jobs_completed` -so we'll need to also use another type that will only allow one thread to -mutate the data at a time. Take a look at this section of the book: +to **immutable** data. But we want to *change* the number of `jobs_done` so +we'll need to also use another type that will only allow one thread to mutate +the data at a time. Take a look at this section of the book: https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct Keep reading if you'd like more hints :) Do you now have an `Arc>` at the beginning of `main`? Like: ``` -let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); +let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 })); ``` -Similar to the code in the following example in the book: +Similar to the code in the following example in The Book: https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads""" [[exercises]] diff --git a/solutions/20_threads/threads2.rs b/solutions/20_threads/threads2.rs index 4e18198..bc268d6 100644 --- a/solutions/20_threads/threads2.rs +++ b/solutions/20_threads/threads2.rs @@ -1 +1,41 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// Building on the last exercise, we want all of the threads to complete their +// work. But this time, the spawned threads need to be in charge of updating a +// shared value: `JobStatus.jobs_done` + +use std::{ + sync::{Arc, Mutex}, + thread, + time::Duration, +}; + +struct JobStatus { + jobs_done: u32, +} + +fn main() { + // `Arc` isn't enough if you want a **mutable** shared state. + // We need to wrap the value with a `Mutex`. + let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 })); + // ^^^^^^^^^^^ ^ + + let mut handles = Vec::new(); + for _ in 0..10 { + let status_shared = Arc::clone(&status); + let handle = thread::spawn(move || { + thread::sleep(Duration::from_millis(250)); + + // Lock before you update a shared value. + status_shared.lock().unwrap().jobs_done += 1; + // ^^^^^^^^^^^^^^^^ + }); + handles.push(handle); + } + + // Waiting for all jobs to complete. + for handle in handles { + handle.join().unwrap(); + } + + println!("Jobs done: {}", status.lock().unwrap().jobs_done); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} -- cgit v1.2.3 From a13e3cd07f86e8668a326bae98568cced61f5015 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:23:40 +0200 Subject: threads3 solution --- exercises/20_threads/threads3.rs | 23 +++++++------- rustlings-macros/info.toml | 5 +-- solutions/20_threads/threads3.rs | 67 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 15 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index 37810cf..30ac8dd 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -1,7 +1,4 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::thread; -use std::time::Duration; +use std::{sync::mpsc, thread, time::Duration}; struct Queue { length: u32, @@ -11,7 +8,7 @@ struct Queue { impl Queue { fn new() -> Self { - Queue { + Self { length: 10, first_half: vec![1, 2, 3, 4, 5], second_half: vec![6, 7, 8, 9, 10], @@ -19,20 +16,22 @@ impl Queue { } } -fn send_tx(q: Queue, tx: mpsc::Sender) -> () { +fn send_tx(q: Queue, tx: mpsc::Sender) { + // TODO: We want to send `tx` to both threads. But currently, it is moved + // into the frist thread. How could you solve this problem? thread::spawn(move || { for val in q.first_half { - println!("sending {:?}", val); + println!("Sending {val:?}"); tx.send(val).unwrap(); - thread::sleep(Duration::from_secs(1)); + thread::sleep(Duration::from_millis(250)); } }); thread::spawn(move || { for val in q.second_half { - println!("sending {:?}", val); + println!("Sending {val:?}"); tx.send(val).unwrap(); - thread::sleep(Duration::from_secs(1)); + thread::sleep(Duration::from_millis(250)); } }); } @@ -55,11 +54,11 @@ mod tests { let mut total_received: u32 = 0; for received in rx { - println!("Got: {}", received); + println!("Got: {received}"); total_received += 1; } - println!("total numbers received: {}", total_received); + println!("Number of received values: {total_received}"); assert_eq!(total_received, queue_length); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index ab8c121..24dcdee 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1076,10 +1076,11 @@ An alternate way to handle concurrency between threads is to use an `mpsc` With both a sending end and a receiving end, it's possible to send values in one thread and receive them in another. -Multiple producers are possible by using clone() to create a duplicate of the +Multiple producers are possible by using `clone()` to create a duplicate of the original sending end. -See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info.""" +Related section in The Book: +https://doc.rust-lang.org/book/ch16-02-message-passing.html""" # MACROS diff --git a/solutions/20_threads/threads3.rs b/solutions/20_threads/threads3.rs index 4e18198..cd2dfbe 100644 --- a/solutions/20_threads/threads3.rs +++ b/solutions/20_threads/threads3.rs @@ -1 +1,66 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +use std::{sync::mpsc, thread, time::Duration}; + +struct Queue { + length: u32, + first_half: Vec, + second_half: Vec, +} + +impl Queue { + fn new() -> Self { + Self { + length: 10, + first_half: vec![1, 2, 3, 4, 5], + second_half: vec![6, 7, 8, 9, 10], + } + } +} + +fn send_tx(q: Queue, tx: mpsc::Sender) { + // Clone the sender `tx` first. + let tx_clone = tx.clone(); + thread::spawn(move || { + for val in q.first_half { + println!("Sending {val:?}"); + // Then use the clone in the first thread. This means that + // `tx_clone` is moved to the first thread and `tx` to the second. + tx_clone.send(val).unwrap(); + thread::sleep(Duration::from_millis(250)); + } + }); + + thread::spawn(move || { + for val in q.second_half { + println!("Sending {val:?}"); + tx.send(val).unwrap(); + thread::sleep(Duration::from_millis(250)); + } + }); +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn threads3() { + let (tx, rx) = mpsc::channel(); + let queue = Queue::new(); + let queue_length = queue.length; + + send_tx(queue, tx); + + let mut total_received: u32 = 0; + for received in rx { + println!("Got: {received}"); + total_received += 1; + } + + println!("Number of received values: {total_received}"); + assert_eq!(total_received, queue_length); + } +} -- cgit v1.2.3 From cf90364fd779255074eac9a7d90c53ad657936ba Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:28:38 +0200 Subject: macros1 solution --- exercises/21_macros/macros1.rs | 1 + rustlings-macros/info.toml | 5 ++--- solutions/21_macros/macros1.rs | 11 ++++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/21_macros/macros1.rs b/exercises/21_macros/macros1.rs index 1d415cb..fb3c3ff 100644 --- a/exercises/21_macros/macros1.rs +++ b/exercises/21_macros/macros1.rs @@ -5,5 +5,6 @@ macro_rules! my_macro { } fn main() { + // TODO: Fix the macro call. my_macro(); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 24dcdee..7dcf344 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1089,9 +1089,8 @@ name = "macros1" dir = "21_macros" test = false hint = """ -When you call a macro, you need to add something special compared to a -regular function call. If you're stuck, take a look at what's inside -`my_macro`.""" +When you call a macro, you need to add something special compared to a regular +function call.""" [[exercises]] name = "macros2" diff --git a/solutions/21_macros/macros1.rs b/solutions/21_macros/macros1.rs index 4e18198..1b86156 100644 --- a/solutions/21_macros/macros1.rs +++ b/solutions/21_macros/macros1.rs @@ -1 +1,10 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +macro_rules! my_macro { + () => { + println!("Check out my macro!"); + }; +} + +fn main() { + my_macro!(); + // ^ +} -- cgit v1.2.3 From 4cb15a4cda4791134a75a0462031b5e86b45fa0d Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:37:48 +0200 Subject: macros3 solution --- exercises/21_macros/macros3.rs | 4 ++-- rustlings-macros/info.toml | 5 +---- solutions/21_macros/macros3.rs | 14 +++++++++++++- 3 files changed, 16 insertions(+), 7 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/21_macros/macros3.rs b/exercises/21_macros/macros3.rs index 405c397..9537494 100644 --- a/exercises/21_macros/macros3.rs +++ b/exercises/21_macros/macros3.rs @@ -1,5 +1,5 @@ -// Make me compile, without taking the macro out of the module! - +// TODO: Fix the compiler error without taking the macro definition out of this +// module. mod macros { macro_rules! my_macro { () => { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 7dcf344..0ec5fb2 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1109,10 +1109,7 @@ dir = "21_macros" test = false hint = """ In order to use a macro outside of its module, you need to do something -special to the module to lift the macro out into its parent. - -The same trick also works on "extern crate" statements for crates that have -exported macros, if you've seen any of those around.""" +special to the module to lift the macro out into its parent.""" [[exercises]] name = "macros4" diff --git a/solutions/21_macros/macros3.rs b/solutions/21_macros/macros3.rs index 4e18198..df35be4 100644 --- a/solutions/21_macros/macros3.rs +++ b/solutions/21_macros/macros3.rs @@ -1 +1,13 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// Added the attribute `macro_use` attribute. +#[macro_use] +mod macros { + macro_rules! my_macro { + () => { + println!("Check out my macro!"); + }; + } +} + +fn main() { + my_macro!(); +} -- cgit v1.2.3 From 78728d52387730300475cbe8c83497f603a14faf Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:54:35 +0200 Subject: clippy1 solution --- exercises/22_clippy/clippy1.rs | 16 +++++++--------- rustlings-macros/info.toml | 4 ++-- solutions/22_clippy/clippy1.rs | 18 +++++++++++++++++- 3 files changed, 26 insertions(+), 12 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/22_clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs index f1eaa83..b9d1ec1 100644 --- a/exercises/22_clippy/clippy1.rs +++ b/exercises/22_clippy/clippy1.rs @@ -1,19 +1,17 @@ // The Clippy tool is a collection of lints to analyze your code so you can // catch common mistakes and improve your Rust code. // -// For these exercises the code will fail to compile when there are Clippy +// For these exercises, the code will fail to compile when there are Clippy // warnings. Check Clippy's suggestions from the output to solve the exercise. -use std::f32; +use std::f32::consts::PI; fn main() { - let pi = 3.14f32; - let radius = 5.00f32; + // Use the more accurate `PI` constant. + let pi = PI; + let radius: f32 = 5.0; - let area = pi * f32::powi(radius, 2); + let area = pi * radius.powi(2); - println!( - "The area of a circle with radius {:.2} is {:.5}!", - radius, area - ) + println!("The area of a circle with radius {radius:.2} is {area:.5}"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 0ec5fb2..4d40726 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1134,7 +1134,7 @@ dir = "22_clippy" test = false strict_clippy = true hint = """ -Rust stores the highest precision version of any long or infinite precision +Rust stores the highest precision version of some long or infinite precision mathematical constants in the Rust standard library: https://doc.rust-lang.org/stable/std/f32/consts/index.html @@ -1142,7 +1142,7 @@ We may be tempted to use our own approximations for certain mathematical constants, but clippy recognizes those imprecise mathematical constants as a source of potential error. -See the suggestions of the clippy warning in compile output and use the +See the suggestions of the Clippy warning in the compile output and use the appropriate replacement constant from `std::f32::consts`...""" [[exercises]] diff --git a/solutions/22_clippy/clippy1.rs b/solutions/22_clippy/clippy1.rs index 4e18198..b9d1ec1 100644 --- a/solutions/22_clippy/clippy1.rs +++ b/solutions/22_clippy/clippy1.rs @@ -1 +1,17 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// The Clippy tool is a collection of lints to analyze your code so you can +// catch common mistakes and improve your Rust code. +// +// For these exercises, the code will fail to compile when there are Clippy +// warnings. Check Clippy's suggestions from the output to solve the exercise. + +use std::f32::consts::PI; + +fn main() { + // Use the more accurate `PI` constant. + let pi = PI; + let radius: f32 = 5.0; + + let area = pi * radius.powi(2); + + println!("The area of a circle with radius {radius:.2} is {area:.5}"); +} -- cgit v1.2.3 From a0e810b4713bcef60f64f4709ee27c3acec676cd Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:55:18 +0200 Subject: clippy2 solution --- exercises/22_clippy/clippy2.rs | 4 +++- rustlings-macros/info.toml | 3 ++- solutions/22_clippy/clippy2.rs | 11 ++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/22_clippy/clippy2.rs b/exercises/22_clippy/clippy2.rs index c7d400d..8cfe6f8 100644 --- a/exercises/22_clippy/clippy2.rs +++ b/exercises/22_clippy/clippy2.rs @@ -1,8 +1,10 @@ fn main() { let mut res = 42; let option = Some(12); + // TODO: Fix the Clippy lint. for x in option { res += x; } - println!("{}", res); + + println!("{res}"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 4d40726..fce5e5a 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1151,7 +1151,8 @@ dir = "22_clippy" test = false strict_clippy = true hint = """ -`for` loops over `Option` values are more clearly expressed as an `if let`""" +`for` loops over `Option` values are more clearly expressed as an `if-let` +statement.""" [[exercises]] name = "clippy3" diff --git a/solutions/22_clippy/clippy2.rs b/solutions/22_clippy/clippy2.rs index 4e18198..7f63562 100644 --- a/solutions/22_clippy/clippy2.rs +++ b/solutions/22_clippy/clippy2.rs @@ -1 +1,10 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +fn main() { + let mut res = 42; + let option = Some(12); + // Use `if-let` instead of iteration. + if let Some(x) = option { + res += x; + } + + println!("{res}"); +} -- cgit v1.2.3 From cddaf4881ea5a03e6deebfa9ec949347e1c2d025 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 22:09:48 +0200 Subject: from_into solution --- exercises/23_conversions/from_into.rs | 62 ++++++++------- rustlings-macros/info.toml | 2 +- solutions/23_conversions/from_into.rs | 137 +++++++++++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 34 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index 14a62ba..bc2783a 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -1,81 +1,79 @@ -// The From trait is used for value-to-value conversions. If From is implemented -// correctly for a type, the Into trait should work conversely. You can read -// more about it at https://doc.rust-lang.org/std/convert/trait.From.html +// The `From` trait is used for value-to-value conversions. If `From` is +// implemented, an implementation of `Into` is automatically provided. +// You can read more about it in the documentation: +// https://doc.rust-lang.org/std/convert/trait.From.html #[derive(Debug)] struct Person { name: String, - age: usize, + age: u8, } -// We implement the Default trait to use it as a fallback -// when the provided string is not convertible into a Person object +// We implement the Default trait to use it as a fallback when the provided +// string is not convertible into a `Person` object. impl Default for Person { - fn default() -> Person { - Person { + fn default() -> Self { + Self { name: String::from("John"), age: 30, } } } -// Your task is to complete this implementation in order for the line `let p1 = -// Person::from("Mark,20")` to compile. Please note that you'll need to parse the -// age component into a `usize` with something like `"4".parse::()`. The -// outcome of this needs to be handled appropriately. +// TODO: Complete this `From` implementation to be able to parse a `Person` +// out of a string in the form of "Mark,20". +// Note that you'll need to parse the age component into a `u8` with something +// like `"4".parse::()`. // // Steps: -// 1. If the length of the provided string is 0, then return the default of -// Person. -// 2. Split the given string on the commas present in it. -// 3. Extract the first element from the split operation and use it as the name. -// 4. If the name is empty, then return the default of Person. -// 5. Extract the other element from the split operation and parse it into a -// `usize` as the age. -// If while parsing the age, something goes wrong, then return the default of -// Person. Otherwise, then return an instantiated Person object with the results - +// 1. Split the given string on the commas present in it. +// 2. If the split operation returns less or more than 2 elements, return the +// default of `Person`. +// 3. Use the first element from the split operation as the name. +// 4. If the name is empty, return the default of `Person`. +// 5. Parse the second element from the split operation into a `u8` as the age. +// 6. If parsing the age fails, return the default of `Person`. impl From<&str> for Person { - fn from(s: &str) -> Person {} + fn from(s: &str) -> Self {} } fn main() { - // Use the `from` function + // Use the `from` function. let p1 = Person::from("Mark,20"); - // Since From is implemented for Person, we should be able to use Into + println!("{p1:?}"); + + // Since `From` is implemented for Person, we are able to use `Into`. let p2: Person = "Gerald,70".into(); - println!("{:?}", p1); - println!("{:?}", p2); + println!("{p2:?}"); } #[cfg(test)] mod tests { use super::*; + #[test] fn test_default() { - // Test that the default person is 30 year old John let dp = Person::default(); assert_eq!(dp.name, "John"); assert_eq!(dp.age, 30); } + #[test] fn test_bad_convert() { - // Test that John is returned when bad string is provided let p = Person::from(""); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } + #[test] fn test_good_convert() { - // Test that "Mark,20" works let p = Person::from("Mark,20"); assert_eq!(p.name, "Mark"); assert_eq!(p.age, 20); } + #[test] fn test_bad_age() { - // Test that "Mark,twenty" will return the default person due to an - // error in parsing age let p = Person::from("Mark,twenty"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index fce5e5a..b848e0e 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1174,7 +1174,7 @@ Use the `as` operator to cast one of the operands in the last line of the name = "from_into" dir = "23_conversions" hint = """ -Follow the steps provided right before the `From` implementation""" +Follow the steps provided right before the `From` implementation.""" [[exercises]] name = "from_str" diff --git a/solutions/23_conversions/from_into.rs b/solutions/23_conversions/from_into.rs index 4e18198..cec23cb 100644 --- a/solutions/23_conversions/from_into.rs +++ b/solutions/23_conversions/from_into.rs @@ -1 +1,136 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// The `From` trait is used for value-to-value conversions. If `From` is +// implemented, an implementation of `Into` is automatically provided. +// You can read more about it in the documentation: +// https://doc.rust-lang.org/std/convert/trait.From.html + +#[derive(Debug)] +struct Person { + name: String, + age: u8, +} + +// We implement the Default trait to use it as a fallback when the provided +// string is not convertible into a `Person` object. +impl Default for Person { + fn default() -> Self { + Self { + name: String::from("John"), + age: 30, + } + } +} + +impl From<&str> for Person { + fn from(s: &str) -> Self { + let mut split = s.split(','); + let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else { + // ^^^^ there should be no third element + return Self::default(); + }; + + if name.is_empty() { + return Self::default(); + } + + let Ok(age) = age.parse() else { + return Self::default(); + }; + + Self { + name: name.into(), + age, + } + } +} + +fn main() { + // Use the `from` function. + let p1 = Person::from("Mark,20"); + println!("{p1:?}"); + + // Since `From` is implemented for Person, we are able to use `Into`. + let p2: Person = "Gerald,70".into(); + println!("{p2:?}"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default() { + let dp = Person::default(); + assert_eq!(dp.name, "John"); + assert_eq!(dp.age, 30); + } + + #[test] + fn test_bad_convert() { + let p = Person::from(""); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_good_convert() { + let p = Person::from("Mark,20"); + assert_eq!(p.name, "Mark"); + assert_eq!(p.age, 20); + } + + #[test] + fn test_bad_age() { + let p = Person::from("Mark,twenty"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_comma_and_age() { + let p: Person = Person::from("Mark"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_age() { + let p: Person = Person::from("Mark,"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_name() { + let p: Person = Person::from(",1"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_name_and_age() { + let p: Person = Person::from(","); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_name_and_invalid_age() { + let p: Person = Person::from(",one"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_trailing_comma() { + let p: Person = Person::from("Mike,32,"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_trailing_comma_and_some_string() { + let p: Person = Person::from("Mike,32,dog"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } +} -- cgit v1.2.3 From e3c8c457ba8744b0f1b799c4d7d4bf24e8e61792 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 01:03:55 +0200 Subject: from_str solution --- exercises/23_conversions/from_str.rs | 60 ++++++++-------- rustlings-macros/info.toml | 10 ++- solutions/23_conversions/from_str.rs | 129 ++++++++++++++++++++++++++++++++++- 3 files changed, 163 insertions(+), 36 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index 58270f0..1b3f553 100644 --- a/exercises/23_conversions/from_str.rs +++ b/exercises/23_conversions/from_str.rs @@ -1,7 +1,8 @@ -// This is similar to from_into.rs, but this time we'll implement `FromStr` and -// return errors instead of falling back to a default value. Additionally, upon -// implementing FromStr, you can use the `parse` method on strings to generate -// an object of the implementor type. You can read more about it at +// This is similar to the previous `from_into` exercise. But this time, we'll +// implement `FromStr` and return errors instead of falling back to a default +// value. Additionally, upon implementing `FromStr`, you can use the `parse` +// method on strings to generate an object of the implementor type. You can read +// more about it in the documentation: // https://doc.rust-lang.org/std/str/trait.FromStr.html use std::num::ParseIntError; @@ -10,43 +11,42 @@ use std::str::FromStr; #[derive(Debug, PartialEq)] struct Person { name: String, - age: usize, + age: u8, } // We will use this error type for the `FromStr` implementation. #[derive(Debug, PartialEq)] enum ParsePersonError { - // Empty input string - Empty, // Incorrect number of fields BadLen, // Empty name field NoName, - // Wrapped error from parse::() + // Wrapped error from parse::() ParseInt(ParseIntError), } +// TODO: Complete this `From` implementation to be able to parse a `Person` +// out of a string in the form of "Mark,20". +// Note that you'll need to parse the age component into a `u8` with something +// like `"4".parse::()`. +// // Steps: -// 1. If the length of the provided string is 0, an error should be returned -// 2. Split the given string on the commas present in it -// 3. Only 2 elements should be returned from the split, otherwise return an -// error -// 4. Extract the first element from the split operation and use it as the name -// 5. Extract the other element from the split operation and parse it into a -// `usize` as the age with something like `"4".parse::()` -// 6. If while extracting the name and the age something goes wrong, an error -// should be returned -// If everything goes well, then return a Result of a Person object - +// 1. Split the given string on the commas present in it. +// 2. If the split operation returns less or more than 2 elements, return the +// error `ParsePersonError::BadLen`. +// 3. Use the first element from the split operation as the name. +// 4. If the name is empty, return the error `ParsePersonError::NoName`. +// 5. Parse the second element from the split operation into a `u8` as the age. +// 6. If parsing the age fails, return the error `ParsePersonError::ParseInt`. impl FromStr for Person { type Err = ParsePersonError; - fn from_str(s: &str) -> Result { - } + + fn from_str(s: &str) -> Result {} } fn main() { - let p = "Mark,20".parse::().unwrap(); - println!("{:?}", p); + let p = "Mark,20".parse::(); + println!("{p:?}"); } #[cfg(test)] @@ -55,8 +55,9 @@ mod tests { #[test] fn empty_input() { - assert_eq!("".parse::(), Err(ParsePersonError::Empty)); + assert_eq!("".parse::(), Err(ParsePersonError::BadLen)); } + #[test] fn good_input() { let p = "John,32".parse::(); @@ -65,11 +66,12 @@ mod tests { assert_eq!(p.name, "John"); assert_eq!(p.age, 32); } + #[test] fn missing_age() { assert!(matches!( "John,".parse::(), - Err(ParsePersonError::ParseInt(_)) + Err(ParsePersonError::ParseInt(_)), )); } @@ -77,7 +79,7 @@ mod tests { fn invalid_age() { assert!(matches!( "John,twenty".parse::(), - Err(ParsePersonError::ParseInt(_)) + Err(ParsePersonError::ParseInt(_)), )); } @@ -95,7 +97,7 @@ mod tests { fn missing_name_and_age() { assert!(matches!( ",".parse::(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)) + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), )); } @@ -103,7 +105,7 @@ mod tests { fn missing_name_and_invalid_age() { assert!(matches!( ",one".parse::(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)) + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), )); } @@ -116,7 +118,7 @@ mod tests { fn trailing_comma_and_some_string() { assert_eq!( "John,32,man".parse::(), - Err(ParsePersonError::BadLen) + Err(ParsePersonError::BadLen), ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index b848e0e..4ef1a0a 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1183,13 +1183,11 @@ hint = """ The implementation of `FromStr` should return an `Ok` with a `Person` object, or an `Err` with an error if the string is not valid. -This is almost like the `from_into` exercise, but returning errors instead -of falling back to a default value. +This is almost like the previous `from_into` exercise, but returning errors +instead of falling back to a default value. -Look at the test cases to see which error variants to return. - -Another hint: You can use the `map_err` method of `Result` with a function -or a closure to wrap the error from `parse::`. +Another hint: You can use the `map_err` method of `Result` with a function or a +closure to wrap the error from `parse::`. Yet another hint: If you would like to propagate errors by using the `?` operator in your solution, you might want to look at diff --git a/solutions/23_conversions/from_str.rs b/solutions/23_conversions/from_str.rs index 4e18198..301150b 100644 --- a/solutions/23_conversions/from_str.rs +++ b/solutions/23_conversions/from_str.rs @@ -1 +1,128 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// This is similar to the previous `from_into` exercise. But this time, we'll +// implement `FromStr` and return errors instead of falling back to a default +// value. Additionally, upon implementing `FromStr`, you can use the `parse` +// method on strings to generate an object of the implementor type. You can read +// more about it in the documentation: +// https://doc.rust-lang.org/std/str/trait.FromStr.html + +use std::num::ParseIntError; +use std::str::FromStr; + +#[derive(Debug, PartialEq)] +struct Person { + name: String, + age: u8, +} + +// We will use this error type for the `FromStr` implementation. +#[derive(Debug, PartialEq)] +enum ParsePersonError { + // Incorrect number of fields + BadLen, + // Empty name field + NoName, + // Wrapped error from parse::() + ParseInt(ParseIntError), +} + +impl FromStr for Person { + type Err = ParsePersonError; + + fn from_str(s: &str) -> Result { + let mut split = s.split(','); + let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else { + // ^^^^ there should be no third element + return Err(ParsePersonError::BadLen); + }; + + if name.is_empty() { + return Err(ParsePersonError::NoName); + } + + let age = age.parse().map_err(ParsePersonError::ParseInt)?; + + Ok(Self { + name: name.into(), + age, + }) + } +} + +fn main() { + let p = "Mark,20".parse::(); + println!("{p:?}"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_input() { + assert_eq!("".parse::(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn good_input() { + let p = "John,32".parse::(); + assert!(p.is_ok()); + let p = p.unwrap(); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 32); + } + + #[test] + fn missing_age() { + assert!(matches!( + "John,".parse::(), + Err(ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn invalid_age() { + assert!(matches!( + "John,twenty".parse::(), + Err(ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn missing_comma_and_age() { + assert_eq!("John".parse::(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn missing_name() { + assert_eq!(",1".parse::(), Err(ParsePersonError::NoName)); + } + + #[test] + fn missing_name_and_age() { + assert!(matches!( + ",".parse::(), + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn missing_name_and_invalid_age() { + assert!(matches!( + ",one".parse::(), + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn trailing_comma() { + assert_eq!("John,32,".parse::(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn trailing_comma_and_some_string() { + assert_eq!( + "John,32,man".parse::(), + Err(ParsePersonError::BadLen), + ); + } +} -- cgit v1.2.3 From 5217cdc5e2c49d179497e5ef65d0dc8bff1e0950 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 01:26:09 +0200 Subject: try_from_into solution --- exercises/23_conversions/try_from_into.rs | 113 ++++++++--------- rustlings-macros/info.toml | 17 +-- solutions/23_conversions/try_from_into.rs | 193 +++++++++++++++++++++++++++++- 3 files changed, 246 insertions(+), 77 deletions(-) (limited to 'rustlings-macros') diff --git a/exercises/23_conversions/try_from_into.rs b/exercises/23_conversions/try_from_into.rs index da45e5a..f3ae80a 100644 --- a/exercises/23_conversions/try_from_into.rs +++ b/exercises/23_conversions/try_from_into.rs @@ -1,9 +1,10 @@ -// TryFrom is a simple and safe type conversion that may fail in a controlled -// way under some circumstances. Basically, this is the same as From. The main -// difference is that this should return a Result type instead of the target -// type itself. You can read more about it at +// `TryFrom` is a simple and safe type conversion that may fail in a controlled +// way under some circumstances. Basically, this is the same as `From`. The main +// difference is that this should return a `Result` type instead of the target +// type itself. You can read more about it in the documentation: // https://doc.rust-lang.org/std/convert/trait.TryFrom.html +#![allow(clippy::useless_vec)] use std::convert::{TryFrom, TryInto}; #[derive(Debug, PartialEq)] @@ -13,7 +14,7 @@ struct Color { blue: u8, } -// We will use this error type for these `TryFrom` conversions. +// We will use this error type for the `TryFrom` conversions. #[derive(Debug, PartialEq)] enum IntoColorError { // Incorrect length of slice @@ -22,78 +23,67 @@ enum IntoColorError { IntConversion, } -// Your task is to complete this implementation and return an Ok result of inner -// type Color. You need to create an implementation for a tuple of three -// integers, an array of three integers, and a slice of integers. -// -// Note that the implementation for tuple and array will be checked at compile -// time, but the slice implementation needs to check the slice length! Also note -// that correct RGB color values must be integers in the 0..=255 range. - -// Tuple implementation +// TODO: Tuple implementation. +// Correct RGB color values must be integers in the 0..=255 range. impl TryFrom<(i16, i16, i16)> for Color { type Error = IntoColorError; - fn try_from(tuple: (i16, i16, i16)) -> Result { - } + + fn try_from(tuple: (i16, i16, i16)) -> Result {} } -// Array implementation +// TODO: Array implementation. impl TryFrom<[i16; 3]> for Color { type Error = IntoColorError; - fn try_from(arr: [i16; 3]) -> Result { - } + + fn try_from(arr: [i16; 3]) -> Result {} } -// Slice implementation +// TODO: Slice implementation. +// This implementation needs to check the slice length. impl TryFrom<&[i16]> for Color { type Error = IntoColorError; - fn try_from(slice: &[i16]) -> Result { - } + + fn try_from(slice: &[i16]) -> Result {} } fn main() { - // Use the `try_from` function + // Using the `try_from` function. let c1 = Color::try_from((183, 65, 14)); - println!("{:?}", c1); + println!("{c1:?}"); - // Since TryFrom is implemented for Color, we should be able to use TryInto + // Since `TryFrom` is implemented for `Color`, we can use `TryInto`. let c2: Result = [183, 65, 14].try_into(); - println!("{:?}", c2); + println!("{c2:?}"); let v = vec![183, 65, 14]; - // With slice we should use `try_from` function + // With slice we should use the `try_from` function let c3 = Color::try_from(&v[..]); - println!("{:?}", c3); - // or take slice within round brackets and use TryInto + println!("{c3:?}"); + // or put the slice within round brackets and use `try_into`. let c4: Result = (&v[..]).try_into(); - println!("{:?}", c4); + println!("{c4:?}"); } #[cfg(test)] mod tests { use super::*; + use IntoColorError::*; #[test] fn test_tuple_out_of_range_positive() { - assert_eq!( - Color::try_from((256, 1000, 10000)), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from((256, 1000, 10000)), Err(IntConversion)); } + #[test] fn test_tuple_out_of_range_negative() { - assert_eq!( - Color::try_from((-1, -10, -256)), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion)); } + #[test] fn test_tuple_sum() { - assert_eq!( - Color::try_from((-1, 255, 255)), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion)); } + #[test] fn test_tuple_correct() { let c: Result = (183, 65, 14).try_into(); @@ -103,25 +93,29 @@ mod tests { Color { red: 183, green: 65, - blue: 14 + blue: 14, } ); } + #[test] fn test_array_out_of_range_positive() { let c: Result = [1000, 10000, 256].try_into(); - assert_eq!(c, Err(IntoColorError::IntConversion)); + assert_eq!(c, Err(IntConversion)); } + #[test] fn test_array_out_of_range_negative() { let c: Result = [-10, -256, -1].try_into(); - assert_eq!(c, Err(IntoColorError::IntConversion)); + assert_eq!(c, Err(IntConversion)); } + #[test] fn test_array_sum() { let c: Result = [-1, 255, 255].try_into(); - assert_eq!(c, Err(IntoColorError::IntConversion)); + assert_eq!(c, Err(IntConversion)); } + #[test] fn test_array_correct() { let c: Result = [183, 65, 14].try_into(); @@ -135,30 +129,25 @@ mod tests { } ); } + #[test] fn test_slice_out_of_range_positive() { let arr = [10000, 256, 1000]; - assert_eq!( - Color::try_from(&arr[..]), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } + #[test] fn test_slice_out_of_range_negative() { let arr = [-256, -1, -10]; - assert_eq!( - Color::try_from(&arr[..]), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } + #[test] fn test_slice_sum() { let arr = [-1, 255, 255]; - assert_eq!( - Color::try_from(&arr[..]), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } + #[test] fn test_slice_correct() { let v = vec![183, 65, 14]; @@ -169,18 +158,20 @@ mod tests { Color { red: 183, green: 65, - blue: 14 + blue: 14, } ); } + #[test] fn test_slice_excess_length() { let v = vec![0, 0, 0, 0]; - assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen)); + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); } + #[test] fn test_slice_insufficient_length() { let v = vec![0, 0]; - assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen)); + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 4ef1a0a..488fdac 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1197,21 +1197,8 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen name = "try_from_into" dir = "23_conversions" hint = """ -Follow the steps provided right before the `TryFrom` implementation. -You can also use the example at -https://doc.rust-lang.org/std/convert/trait.TryFrom.html - -Is there an implementation of `TryFrom` in the standard library that -can both do the required integer conversion and check the range of the input? - -Another hint: Look at the test cases to see which error variants to return. - -Yet another hint: You can use the `map_err` or `or` methods of `Result` to -convert errors. - -Yet another hint: If you would like to propagate errors by using the `?` -operator in your solution, you might want to look at -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html +Is there an implementation of `TryFrom` in the standard library that can both do +the required integer conversion and check the range of the input? Challenge: Can you make the `TryFrom` implementations generic over many integer types?""" diff --git a/solutions/23_conversions/try_from_into.rs b/solutions/23_conversions/try_from_into.rs index 4e18198..acb7721 100644 --- a/solutions/23_conversions/try_from_into.rs +++ b/solutions/23_conversions/try_from_into.rs @@ -1 +1,192 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 +// `TryFrom` is a simple and safe type conversion that may fail in a controlled +// way under some circumstances. Basically, this is the same as `From`. The main +// difference is that this should return a `Result` type instead of the target +// type itself. You can read more about it in the documentation: +// https://doc.rust-lang.org/std/convert/trait.TryFrom.html + +use std::convert::{TryFrom, TryInto}; + +#[derive(Debug, PartialEq)] +struct Color { + red: u8, + green: u8, + blue: u8, +} + +// We will use this error type for the `TryFrom` conversions. +#[derive(Debug, PartialEq)] +enum IntoColorError { + // Incorrect length of slice + BadLen, + // Integer conversion error + IntConversion, +} + +impl TryFrom<(i16, i16, i16)> for Color { + type Error = IntoColorError; + + fn try_from(tuple: (i16, i16, i16)) -> Result { + let (Ok(red), Ok(green), Ok(blue)) = ( + u8::try_from(tuple.0), + u8::try_from(tuple.1), + u8::try_from(tuple.2), + ) else { + return Err(IntoColorError::IntConversion); + }; + + Ok(Self { red, green, blue }) + } +} + +impl TryFrom<[i16; 3]> for Color { + type Error = IntoColorError; + + fn try_from(arr: [i16; 3]) -> Result { + // Reuse the implementation for a tuple. + Self::try_from((arr[0], arr[1], arr[2])) + } +} + +impl TryFrom<&[i16]> for Color { + type Error = IntoColorError; + + fn try_from(slice: &[i16]) -> Result { + // Check the length. + if slice.len() != 3 { + return Err(IntoColorError::BadLen); + } + + // Reuse the implementation for a tuple. + Self::try_from((slice[0], slice[1], slice[2])) + } +} + +fn main() { + // Using the `try_from` function. + let c1 = Color::try_from((183, 65, 14)); + println!("{c1:?}"); + + // Since `TryFrom` is implemented for `Color`, we can use `TryInto`. + let c2: Result = [183, 65, 14].try_into(); + println!("{c2:?}"); + + let v = vec![183, 65, 14]; + // With slice we should use the `try_from` function + let c3 = Color::try_from(&v[..]); + println!("{c3:?}"); + // or put the slice within round brackets and use `try_into`. + let c4: Result = (&v[..]).try_into(); + println!("{c4:?}"); +} + +#[cfg(test)] +mod tests { + use super::*; + use IntoColorError::*; + + #[test] + fn test_tuple_out_of_range_positive() { + assert_eq!(Color::try_from((256, 1000, 10000)), Err(IntConversion)); + } + + #[test] + fn test_tuple_out_of_range_negative() { + assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion)); + } + + #[test] + fn test_tuple_sum() { + assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion)); + } + + #[test] + fn test_tuple_correct() { + let c: Result = (183, 65, 14).try_into(); + assert!(c.is_ok()); + assert_eq!( + c.unwrap(), + Color { + red: 183, + green: 65, + blue: 14, + } + ); + } + + #[test] + fn test_array_out_of_range_positive() { + let c: Result = [1000, 10000, 256].try_into(); + assert_eq!(c, Err(IntConversion)); + } + + #[test] + fn test_array_out_of_range_negative() { + let c: Result = [-10, -256, -1].try_into(); + assert_eq!(c, Err(IntConversion)); + } + + #[test] + fn test_array_sum() { + let c: Result = [-1, 255, 255].try_into(); + assert_eq!(c, Err(IntConversion)); + } + + #[test] + fn test_array_correct() { + let c: Result = [183, 65, 14].try_into(); + assert!(c.is_ok()); + assert_eq!( + c.unwrap(), + Color { + red: 183, + green: 65, + blue: 14 + } + ); + } + + #[test] + fn test_slice_out_of_range_positive() { + let arr = [10000, 256, 1000]; + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); + } + + #[test] + fn test_slice_out_of_range_negative() { + let arr = [-256, -1, -10]; + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); + } + + #[test] + fn test_slice_sum() { + let arr = [-1, 255, 255]; + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); + } + + #[test] + fn test_slice_correct() { + let v = vec![183, 65, 14]; + let c: Result = Color::try_from(&v[..]); + assert!(c.is_ok()); + assert_eq!( + c.unwrap(), + Color { + red: 183, + green: 65, + blue: 14, + } + ); + } + + #[test] + fn test_slice_excess_length() { + let v = vec![0, 0, 0, 0]; + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); + } + + #[test] + fn test_slice_insufficient_length() { + let v = vec![0, 0]; + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); + } +} -- cgit v1.2.3 From 2f60f4d9ea23679f81b7f91f1b8bb59d20d304e5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:38:57 +0200 Subject: Remove newline at the end of multiline strings --- rustlings-macros/info.toml | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) (limited to 'rustlings-macros') diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 488fdac..bd73195 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -16,16 +16,14 @@ get started, here are some notes about how Rustlings operates: 3. If you're stuck on an exercise, enter `h` to show a hint. 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, - other learners do too so you can help each other out! -""" + other learners do too so you can help each other out!""" final_message = """We hope you enjoyed learning about the various aspects of Rust! If you noticed any issues, don't hesitate to report them on Github. You can also contribute your own exercises to help the greater community! Before reporting an issue or contributing, please read our guidelines: -https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md -""" +https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md""" # INTRO @@ -130,8 +128,7 @@ The type of Constants must always be annotated. Read more about constants and the differences between variables and constants under 'Constants' in the book's section 'Variables and Mutability': -https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants -""" +https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants""" # FUNCTIONS @@ -312,8 +309,7 @@ In Rust, there are two ways to define a Vector. the initial values. Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html -of the Rust book to learn more. -""" +of the Rust book to learn more.""" [[exercises]] name = "vecs2" @@ -327,8 +323,7 @@ In the second function, we map the values of the input and collect them into a v After you've completed both functions, decide for yourself which approach you like better. -What do you think is the more commonly used pattern under Rust developers? -""" +What do you think is the more commonly used pattern under Rust developers?""" # MOVE SEMANTICS @@ -355,8 +350,7 @@ We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's being "moved" into `vec1`, meaning we can't access `vec0` anymore. You could make another, separate version of the data that's in `vec0` and -pass it to `fill_vec` instead. -""" +pass it to `fill_vec` instead.""" [[exercises]] name = "move_semantics3" @@ -375,8 +369,7 @@ Carefully reason about the range in which each mutable reference is in scope. Does it help to update the value of `x` immediately after the mutable reference is taken? Read more about 'Mutable References' in the book's section 'References and Borrowing': -https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. -""" +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.""" [[exercises]] name = "move_semantics5" @@ -517,8 +510,7 @@ Example: `placeholder("blue");` should become `string_slice("blue");` -because "blue" is `&str`, not `String`. -""" +because "blue" is `&str`, not `String`.""" # MODULES @@ -620,8 +612,7 @@ Remember that `Option`s can be nested in if-let and while-let statements. For example: `if let Some(Some(x)) = y` -Also see `Option::flatten` -""" +Also see `Option::flatten`""" [[exercises]] name = "options3" @@ -813,8 +804,7 @@ Here is how to specify a trait bound for an implementation block: `impl for Foo { … }` You may need this: -`use std::fmt::Display;` -""" +`use std::fmt::Display;`""" # LIFETIMES -- cgit v1.2.3 From 4bf0ddc0e1b6213b9ddd7b804c19326774cdbf61 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 21:12:57 +0200 Subject: Check exercises unsolved --- rustlings-macros/info.toml | 1 + src/dev/check.rs | 58 +++++++++++++++++++++++++++++++++++++++------- src/info_file.rs | 5 +++- 3 files changed, 55 insertions(+), 9 deletions(-) (limited to 'rustlings-macros') diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index bd73195..d75d73f 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -31,6 +31,7 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md""" name = "intro1" dir = "00_intro" test = false +skip_check_unsolved = true hint = """ Enter `n` to move on to the next exercise. You might need to press ENTER after typing `n`.""" diff --git a/src/dev/check.rs b/src/dev/check.rs index 29b2b67..5c35462 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -162,7 +162,46 @@ fn check_unexpected_files( Ok(()) } -fn check_exercises(info_file: &InfoFile) -> Result<()> { +fn check_exercises_unsolved(info_file: &InfoFile, target_dir: &Path) -> Result<()> { + let error_occurred = AtomicBool::new(false); + + println!( + "Running all exercises to check that they aren't already solved. This may take a while…\n", + ); + thread::scope(|s| { + for exercise_info in &info_file.exercises { + if exercise_info.skip_check_unsolved { + continue; + } + + s.spawn(|| { + let error = |e| { + let mut stderr = io::stderr().lock(); + stderr.write_all(e).unwrap(); + stderr.write_all(b"\nProblem with the exercise ").unwrap(); + stderr.write_all(exercise_info.name.as_bytes()).unwrap(); + stderr.write_all(SEPARATOR).unwrap(); + error_occurred.store(true, atomic::Ordering::Relaxed); + }; + + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); + match exercise_info.run_exercise(&mut output, target_dir) { + Ok(true) => error(b"Already solved!"), + Ok(false) => (), + Err(e) => error(e.to_string().as_bytes()), + } + }); + } + }); + + if error_occurred.load(atomic::Ordering::Relaxed) { + bail!(CHECK_EXERCISES_UNSOLVED_ERR); + } + + Ok(()) +} + +fn check_exercises(info_file: &InfoFile, target_dir: &Path) -> Result<()> { match info_file.format_version.cmp(&CURRENT_FORMAT_VERSION) { Ordering::Less => bail!("`format_version` < {CURRENT_FORMAT_VERSION} (supported version)\nPlease migrate to the latest format version"), Ordering::Greater => bail!("`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\nTry updating the Rustlings program"), @@ -172,15 +211,14 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { let info_file_paths = check_info_file_exercises(info_file)?; check_unexpected_files("exercises", &info_file_paths)?; - Ok(()) + check_exercises_unsolved(info_file, target_dir) } -fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> { - let target_dir = parse_target_dir()?; +fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &Path) -> Result<()> { let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len())); let error_occurred = AtomicBool::new(false); - println!("Running all solutions. This may take a while...\n"); + println!("Running all solutions. This may take a while…\n"); thread::scope(|s| { for exercise_info in &info_file.exercises { s.spawn(|| { @@ -206,7 +244,7 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> } let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - match exercise_info.run_solution(&mut output, &target_dir) { + match exercise_info.run_solution(&mut output, target_dir) { Ok(true) => { paths.lock().unwrap().insert(PathBuf::from(path)); } @@ -242,8 +280,9 @@ pub fn check(require_solutions: bool) -> Result<()> { check_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"")?; } - check_exercises(&info_file)?; - check_solutions(require_solutions, &info_file)?; + let target_dir = parse_target_dir()?; + check_exercises(&info_file, &target_dir)?; + check_solutions(require_solutions, &info_file, &target_dir)?; println!("\nEverything looks fine!"); @@ -252,3 +291,6 @@ pub fn check(require_solutions: bool) -> Result<()> { const SEPARATOR: &[u8] = b"\n========================================================================================\n"; + +const CHECK_EXERCISES_UNSOLVED_ERR: &str = "At least one exercise is already solved or failed to run. See the output above. +If this is an intro exercise that is intended to be already solved, add `skip_check_unsolved = true` to the exercise's metadata in the `info.toml` file."; diff --git a/src/info_file.rs b/src/info_file.rs index f226f73..f27d018 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -11,14 +11,17 @@ pub struct ExerciseInfo { pub name: String, /// Exercise's directory name inside the `exercises/` directory. pub dir: Option, - #[serde(default = "default_true")] /// Run `cargo test` on the exercise. + #[serde(default = "default_true")] pub test: bool, /// Deny all Clippy warnings. #[serde(default)] pub strict_clippy: bool, /// The exercise's hint to be shown to the user on request. pub hint: String, + /// The exercise is already solved. Ignore it when checking that all exercises are unsolved. + #[serde(default)] + pub skip_check_unsolved: bool, } #[inline(always)] const fn default_true() -> bool { -- cgit v1.2.3