├── .gitignore ├── .rustlings-state.txt ├── Cargo.toml ├── README.md ├── exercises ├── 00_intro │ ├── README.md │ ├── intro1.rs │ └── intro2.rs ├── 01_variables │ ├── README.md │ ├── variables1.rs │ ├── variables2.rs │ ├── variables3.rs │ ├── variables4.rs │ ├── variables5.rs │ └── variables6.rs ├── 02_functions │ ├── README.md │ ├── functions1.rs │ ├── functions2.rs │ ├── functions3.rs │ ├── functions4.rs │ └── functions5.rs ├── 03_if │ ├── README.md │ ├── if1.rs │ ├── if2.rs │ └── if3.rs ├── 04_primitive_types │ ├── README.md │ ├── primitive_types1.rs │ ├── primitive_types2.rs │ ├── primitive_types3.rs │ ├── primitive_types4.rs │ ├── primitive_types5.rs │ └── primitive_types6.rs ├── 05_vecs │ ├── README.md │ ├── vecs1.rs │ └── vecs2.rs ├── 06_move_semantics │ ├── README.md │ ├── move_semantics1.rs │ ├── move_semantics2.rs │ ├── move_semantics3.rs │ ├── move_semantics4.rs │ └── move_semantics5.rs ├── 07_structs │ ├── README.md │ ├── structs1.rs │ ├── structs2.rs │ └── structs3.rs ├── 08_enums │ ├── README.md │ ├── enums1.rs │ ├── enums2.rs │ └── enums3.rs ├── 09_strings │ ├── README.md │ ├── strings1.rs │ ├── strings2.rs │ ├── strings3.rs │ └── strings4.rs ├── 10_modules │ ├── README.md │ ├── modules1.rs │ ├── modules2.rs │ └── modules3.rs ├── 11_hashmaps │ ├── README.md │ ├── hashmaps1.rs │ ├── hashmaps2.rs │ └── hashmaps3.rs ├── 12_options │ ├── README.md │ ├── options1.rs │ ├── options2.rs │ └── options3.rs ├── 13_error_handling │ ├── README.md │ ├── errors1.rs │ ├── errors2.rs │ ├── errors3.rs │ ├── errors4.rs │ ├── errors5.rs │ └── errors6.rs ├── 14_generics │ ├── README.md │ ├── generics1.rs │ └── generics2.rs ├── 15_traits │ ├── README.md │ ├── traits1.rs │ ├── traits2.rs │ ├── traits3.rs │ ├── traits4.rs │ └── traits5.rs ├── 16_lifetimes │ ├── README.md │ ├── lifetimes1.rs │ ├── lifetimes2.rs │ └── lifetimes3.rs ├── 17_tests │ ├── README.md │ ├── tests1.rs │ ├── tests2.rs │ └── tests3.rs ├── 18_iterators │ ├── README.md │ ├── iterators1.rs │ ├── iterators2.rs │ ├── iterators3.rs │ ├── iterators4.rs │ └── iterators5.rs ├── 19_smart_pointers │ ├── README.md │ ├── arc1.rs │ ├── box1.rs │ ├── cow1.rs │ └── rc1.rs ├── 20_threads │ ├── README.md │ ├── threads1.rs │ ├── threads2.rs │ └── threads3.rs ├── 21_macros │ ├── README.md │ ├── macros1.rs │ ├── macros2.rs │ ├── macros3.rs │ └── macros4.rs ├── 22_clippy │ ├── README.md │ ├── clippy1.rs │ ├── clippy2.rs │ └── clippy3.rs ├── 23_conversions │ ├── README.md │ ├── as_ref_mut.rs │ ├── from_into.rs │ ├── from_str.rs │ ├── try_from_into.rs │ └── using_as.rs ├── README.md └── quizzes │ ├── README.md │ ├── quiz1.rs │ ├── quiz2.rs │ └── quiz3.rs ├── rust-analyzer.toml └── solutions ├── 00_intro ├── intro1.rs └── intro2.rs ├── 01_variables ├── variables1.rs ├── variables2.rs ├── variables3.rs ├── variables4.rs ├── variables5.rs └── variables6.rs ├── 02_functions ├── functions1.rs ├── functions2.rs ├── functions3.rs ├── functions4.rs └── functions5.rs ├── 03_if ├── if1.rs ├── if2.rs └── if3.rs ├── 04_primitive_types ├── primitive_types1.rs ├── primitive_types2.rs ├── primitive_types3.rs ├── primitive_types4.rs ├── primitive_types5.rs └── primitive_types6.rs ├── 05_vecs ├── vecs1.rs └── vecs2.rs ├── 06_move_semantics ├── move_semantics1.rs ├── move_semantics2.rs ├── move_semantics3.rs ├── move_semantics4.rs └── move_semantics5.rs ├── 07_structs ├── structs1.rs ├── structs2.rs └── structs3.rs ├── 08_enums ├── enums1.rs ├── enums2.rs └── enums3.rs ├── 09_strings ├── strings1.rs ├── strings2.rs ├── strings3.rs └── strings4.rs ├── 10_modules ├── modules1.rs ├── modules2.rs └── modules3.rs ├── 11_hashmaps ├── hashmaps1.rs ├── hashmaps2.rs └── hashmaps3.rs ├── 12_options ├── options1.rs ├── options2.rs └── options3.rs ├── 13_error_handling ├── errors1.rs ├── errors2.rs ├── errors3.rs ├── errors4.rs ├── errors5.rs └── errors6.rs ├── 14_generics ├── generics1.rs └── generics2.rs ├── 15_traits ├── traits1.rs ├── traits2.rs ├── traits3.rs ├── traits4.rs └── traits5.rs ├── 16_lifetimes ├── lifetimes1.rs ├── lifetimes2.rs └── lifetimes3.rs ├── 17_tests ├── tests1.rs ├── tests2.rs └── tests3.rs ├── 18_iterators ├── iterators1.rs ├── iterators2.rs ├── iterators3.rs ├── iterators4.rs └── iterators5.rs ├── 19_smart_pointers ├── arc1.rs ├── box1.rs ├── cow1.rs └── rc1.rs ├── 20_threads ├── threads1.rs ├── threads2.rs └── threads3.rs ├── 21_macros ├── macros1.rs ├── macros2.rs ├── macros3.rs └── macros4.rs ├── 22_clippy ├── clippy1.rs ├── clippy2.rs └── clippy3.rs ├── 23_conversions ├── as_ref_mut.rs ├── from_into.rs ├── from_str.rs ├── try_from_into.rs └── using_as.rs ├── README.md └── quizzes ├── quiz1.rs ├── quiz2.rs └── quiz3.rs /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | .vscode/ 4 | -------------------------------------------------------------------------------- /.rustlings-state.txt: -------------------------------------------------------------------------------- 1 | DON'T EDIT THIS FILE! 2 | 3 | strings4 4 | 5 | intro1 6 | intro2 7 | variables1 8 | variables2 9 | variables3 10 | variables4 11 | variables5 12 | variables6 13 | functions1 14 | functions2 15 | functions3 16 | functions4 17 | functions5 18 | if1 19 | if2 20 | if3 21 | quiz1 22 | primitive_types1 23 | primitive_types2 24 | primitive_types3 25 | primitive_types4 26 | primitive_types5 27 | primitive_types6 28 | vecs1 29 | vecs2 30 | move_semantics1 31 | move_semantics2 32 | move_semantics3 33 | move_semantics4 34 | move_semantics5 35 | structs1 36 | structs2 37 | structs3 38 | enums1 39 | enums2 40 | enums3 41 | strings1 42 | strings2 43 | strings3 -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | bin = [ 2 | { name = "intro1", path = "exercises/00_intro/intro1.rs" }, 3 | { name = "intro1_sol", path = "solutions/00_intro/intro1.rs" }, 4 | { name = "intro2", path = "exercises/00_intro/intro2.rs" }, 5 | { name = "intro2_sol", path = "solutions/00_intro/intro2.rs" }, 6 | { name = "variables1", path = "exercises/01_variables/variables1.rs" }, 7 | { name = "variables1_sol", path = "solutions/01_variables/variables1.rs" }, 8 | { name = "variables2", path = "exercises/01_variables/variables2.rs" }, 9 | { name = "variables2_sol", path = "solutions/01_variables/variables2.rs" }, 10 | { name = "variables3", path = "exercises/01_variables/variables3.rs" }, 11 | { name = "variables3_sol", path = "solutions/01_variables/variables3.rs" }, 12 | { name = "variables4", path = "exercises/01_variables/variables4.rs" }, 13 | { name = "variables4_sol", path = "solutions/01_variables/variables4.rs" }, 14 | { name = "variables5", path = "exercises/01_variables/variables5.rs" }, 15 | { name = "variables5_sol", path = "solutions/01_variables/variables5.rs" }, 16 | { name = "variables6", path = "exercises/01_variables/variables6.rs" }, 17 | { name = "variables6_sol", path = "solutions/01_variables/variables6.rs" }, 18 | { name = "functions1", path = "exercises/02_functions/functions1.rs" }, 19 | { name = "functions1_sol", path = "solutions/02_functions/functions1.rs" }, 20 | { name = "functions2", path = "exercises/02_functions/functions2.rs" }, 21 | { name = "functions2_sol", path = "solutions/02_functions/functions2.rs" }, 22 | { name = "functions3", path = "exercises/02_functions/functions3.rs" }, 23 | { name = "functions3_sol", path = "solutions/02_functions/functions3.rs" }, 24 | { name = "functions4", path = "exercises/02_functions/functions4.rs" }, 25 | { name = "functions4_sol", path = "solutions/02_functions/functions4.rs" }, 26 | { name = "functions5", path = "exercises/02_functions/functions5.rs" }, 27 | { name = "functions5_sol", path = "solutions/02_functions/functions5.rs" }, 28 | { name = "if1", path = "exercises/03_if/if1.rs" }, 29 | { name = "if1_sol", path = "solutions/03_if/if1.rs" }, 30 | { name = "if2", path = "exercises/03_if/if2.rs" }, 31 | { name = "if2_sol", path = "solutions/03_if/if2.rs" }, 32 | { name = "if3", path = "exercises/03_if/if3.rs" }, 33 | { name = "if3_sol", path = "solutions/03_if/if3.rs" }, 34 | { name = "quiz1", path = "exercises/quizzes/quiz1.rs" }, 35 | { name = "quiz1_sol", path = "solutions/quizzes/quiz1.rs" }, 36 | { name = "primitive_types1", path = "exercises/04_primitive_types/primitive_types1.rs" }, 37 | { name = "primitive_types1_sol", path = "solutions/04_primitive_types/primitive_types1.rs" }, 38 | { name = "primitive_types2", path = "exercises/04_primitive_types/primitive_types2.rs" }, 39 | { name = "primitive_types2_sol", path = "solutions/04_primitive_types/primitive_types2.rs" }, 40 | { name = "primitive_types3", path = "exercises/04_primitive_types/primitive_types3.rs" }, 41 | { name = "primitive_types3_sol", path = "solutions/04_primitive_types/primitive_types3.rs" }, 42 | { name = "primitive_types4", path = "exercises/04_primitive_types/primitive_types4.rs" }, 43 | { name = "primitive_types4_sol", path = "solutions/04_primitive_types/primitive_types4.rs" }, 44 | { name = "primitive_types5", path = "exercises/04_primitive_types/primitive_types5.rs" }, 45 | { name = "primitive_types5_sol", path = "solutions/04_primitive_types/primitive_types5.rs" }, 46 | { name = "primitive_types6", path = "exercises/04_primitive_types/primitive_types6.rs" }, 47 | { name = "primitive_types6_sol", path = "solutions/04_primitive_types/primitive_types6.rs" }, 48 | { name = "vecs1", path = "exercises/05_vecs/vecs1.rs" }, 49 | { name = "vecs1_sol", path = "solutions/05_vecs/vecs1.rs" }, 50 | { name = "vecs2", path = "exercises/05_vecs/vecs2.rs" }, 51 | { name = "vecs2_sol", path = "solutions/05_vecs/vecs2.rs" }, 52 | { name = "move_semantics1", path = "exercises/06_move_semantics/move_semantics1.rs" }, 53 | { name = "move_semantics1_sol", path = "solutions/06_move_semantics/move_semantics1.rs" }, 54 | { name = "move_semantics2", path = "exercises/06_move_semantics/move_semantics2.rs" }, 55 | { name = "move_semantics2_sol", path = "solutions/06_move_semantics/move_semantics2.rs" }, 56 | { name = "move_semantics3", path = "exercises/06_move_semantics/move_semantics3.rs" }, 57 | { name = "move_semantics3_sol", path = "solutions/06_move_semantics/move_semantics3.rs" }, 58 | { name = "move_semantics4", path = "exercises/06_move_semantics/move_semantics4.rs" }, 59 | { name = "move_semantics4_sol", path = "solutions/06_move_semantics/move_semantics4.rs" }, 60 | { name = "move_semantics5", path = "exercises/06_move_semantics/move_semantics5.rs" }, 61 | { name = "move_semantics5_sol", path = "solutions/06_move_semantics/move_semantics5.rs" }, 62 | { name = "structs1", path = "exercises/07_structs/structs1.rs" }, 63 | { name = "structs1_sol", path = "solutions/07_structs/structs1.rs" }, 64 | { name = "structs2", path = "exercises/07_structs/structs2.rs" }, 65 | { name = "structs2_sol", path = "solutions/07_structs/structs2.rs" }, 66 | { name = "structs3", path = "exercises/07_structs/structs3.rs" }, 67 | { name = "structs3_sol", path = "solutions/07_structs/structs3.rs" }, 68 | { name = "enums1", path = "exercises/08_enums/enums1.rs" }, 69 | { name = "enums1_sol", path = "solutions/08_enums/enums1.rs" }, 70 | { name = "enums2", path = "exercises/08_enums/enums2.rs" }, 71 | { name = "enums2_sol", path = "solutions/08_enums/enums2.rs" }, 72 | { name = "enums3", path = "exercises/08_enums/enums3.rs" }, 73 | { name = "enums3_sol", path = "solutions/08_enums/enums3.rs" }, 74 | { name = "strings1", path = "exercises/09_strings/strings1.rs" }, 75 | { name = "strings1_sol", path = "solutions/09_strings/strings1.rs" }, 76 | { name = "strings2", path = "exercises/09_strings/strings2.rs" }, 77 | { name = "strings2_sol", path = "solutions/09_strings/strings2.rs" }, 78 | { name = "strings3", path = "exercises/09_strings/strings3.rs" }, 79 | { name = "strings3_sol", path = "solutions/09_strings/strings3.rs" }, 80 | { name = "strings4", path = "exercises/09_strings/strings4.rs" }, 81 | { name = "strings4_sol", path = "solutions/09_strings/strings4.rs" }, 82 | { name = "modules1", path = "exercises/10_modules/modules1.rs" }, 83 | { name = "modules1_sol", path = "solutions/10_modules/modules1.rs" }, 84 | { name = "modules2", path = "exercises/10_modules/modules2.rs" }, 85 | { name = "modules2_sol", path = "solutions/10_modules/modules2.rs" }, 86 | { name = "modules3", path = "exercises/10_modules/modules3.rs" }, 87 | { name = "modules3_sol", path = "solutions/10_modules/modules3.rs" }, 88 | { name = "hashmaps1", path = "exercises/11_hashmaps/hashmaps1.rs" }, 89 | { name = "hashmaps1_sol", path = "solutions/11_hashmaps/hashmaps1.rs" }, 90 | { name = "hashmaps2", path = "exercises/11_hashmaps/hashmaps2.rs" }, 91 | { name = "hashmaps2_sol", path = "solutions/11_hashmaps/hashmaps2.rs" }, 92 | { name = "hashmaps3", path = "exercises/11_hashmaps/hashmaps3.rs" }, 93 | { name = "hashmaps3_sol", path = "solutions/11_hashmaps/hashmaps3.rs" }, 94 | { name = "quiz2", path = "exercises/quizzes/quiz2.rs" }, 95 | { name = "quiz2_sol", path = "solutions/quizzes/quiz2.rs" }, 96 | { name = "options1", path = "exercises/12_options/options1.rs" }, 97 | { name = "options1_sol", path = "solutions/12_options/options1.rs" }, 98 | { name = "options2", path = "exercises/12_options/options2.rs" }, 99 | { name = "options2_sol", path = "solutions/12_options/options2.rs" }, 100 | { name = "options3", path = "exercises/12_options/options3.rs" }, 101 | { name = "options3_sol", path = "solutions/12_options/options3.rs" }, 102 | { name = "errors1", path = "exercises/13_error_handling/errors1.rs" }, 103 | { name = "errors1_sol", path = "solutions/13_error_handling/errors1.rs" }, 104 | { name = "errors2", path = "exercises/13_error_handling/errors2.rs" }, 105 | { name = "errors2_sol", path = "solutions/13_error_handling/errors2.rs" }, 106 | { name = "errors3", path = "exercises/13_error_handling/errors3.rs" }, 107 | { name = "errors3_sol", path = "solutions/13_error_handling/errors3.rs" }, 108 | { name = "errors4", path = "exercises/13_error_handling/errors4.rs" }, 109 | { name = "errors4_sol", path = "solutions/13_error_handling/errors4.rs" }, 110 | { name = "errors5", path = "exercises/13_error_handling/errors5.rs" }, 111 | { name = "errors5_sol", path = "solutions/13_error_handling/errors5.rs" }, 112 | { name = "errors6", path = "exercises/13_error_handling/errors6.rs" }, 113 | { name = "errors6_sol", path = "solutions/13_error_handling/errors6.rs" }, 114 | { name = "generics1", path = "exercises/14_generics/generics1.rs" }, 115 | { name = "generics1_sol", path = "solutions/14_generics/generics1.rs" }, 116 | { name = "generics2", path = "exercises/14_generics/generics2.rs" }, 117 | { name = "generics2_sol", path = "solutions/14_generics/generics2.rs" }, 118 | { name = "traits1", path = "exercises/15_traits/traits1.rs" }, 119 | { name = "traits1_sol", path = "solutions/15_traits/traits1.rs" }, 120 | { name = "traits2", path = "exercises/15_traits/traits2.rs" }, 121 | { name = "traits2_sol", path = "solutions/15_traits/traits2.rs" }, 122 | { name = "traits3", path = "exercises/15_traits/traits3.rs" }, 123 | { name = "traits3_sol", path = "solutions/15_traits/traits3.rs" }, 124 | { name = "traits4", path = "exercises/15_traits/traits4.rs" }, 125 | { name = "traits4_sol", path = "solutions/15_traits/traits4.rs" }, 126 | { name = "traits5", path = "exercises/15_traits/traits5.rs" }, 127 | { name = "traits5_sol", path = "solutions/15_traits/traits5.rs" }, 128 | { name = "quiz3", path = "exercises/quizzes/quiz3.rs" }, 129 | { name = "quiz3_sol", path = "solutions/quizzes/quiz3.rs" }, 130 | { name = "lifetimes1", path = "exercises/16_lifetimes/lifetimes1.rs" }, 131 | { name = "lifetimes1_sol", path = "solutions/16_lifetimes/lifetimes1.rs" }, 132 | { name = "lifetimes2", path = "exercises/16_lifetimes/lifetimes2.rs" }, 133 | { name = "lifetimes2_sol", path = "solutions/16_lifetimes/lifetimes2.rs" }, 134 | { name = "lifetimes3", path = "exercises/16_lifetimes/lifetimes3.rs" }, 135 | { name = "lifetimes3_sol", path = "solutions/16_lifetimes/lifetimes3.rs" }, 136 | { name = "tests1", path = "exercises/17_tests/tests1.rs" }, 137 | { name = "tests1_sol", path = "solutions/17_tests/tests1.rs" }, 138 | { name = "tests2", path = "exercises/17_tests/tests2.rs" }, 139 | { name = "tests2_sol", path = "solutions/17_tests/tests2.rs" }, 140 | { name = "tests3", path = "exercises/17_tests/tests3.rs" }, 141 | { name = "tests3_sol", path = "solutions/17_tests/tests3.rs" }, 142 | { name = "iterators1", path = "exercises/18_iterators/iterators1.rs" }, 143 | { name = "iterators1_sol", path = "solutions/18_iterators/iterators1.rs" }, 144 | { name = "iterators2", path = "exercises/18_iterators/iterators2.rs" }, 145 | { name = "iterators2_sol", path = "solutions/18_iterators/iterators2.rs" }, 146 | { name = "iterators3", path = "exercises/18_iterators/iterators3.rs" }, 147 | { name = "iterators3_sol", path = "solutions/18_iterators/iterators3.rs" }, 148 | { name = "iterators4", path = "exercises/18_iterators/iterators4.rs" }, 149 | { name = "iterators4_sol", path = "solutions/18_iterators/iterators4.rs" }, 150 | { name = "iterators5", path = "exercises/18_iterators/iterators5.rs" }, 151 | { name = "iterators5_sol", path = "solutions/18_iterators/iterators5.rs" }, 152 | { name = "box1", path = "exercises/19_smart_pointers/box1.rs" }, 153 | { name = "box1_sol", path = "solutions/19_smart_pointers/box1.rs" }, 154 | { name = "rc1", path = "exercises/19_smart_pointers/rc1.rs" }, 155 | { name = "rc1_sol", path = "solutions/19_smart_pointers/rc1.rs" }, 156 | { name = "arc1", path = "exercises/19_smart_pointers/arc1.rs" }, 157 | { name = "arc1_sol", path = "solutions/19_smart_pointers/arc1.rs" }, 158 | { name = "cow1", path = "exercises/19_smart_pointers/cow1.rs" }, 159 | { name = "cow1_sol", path = "solutions/19_smart_pointers/cow1.rs" }, 160 | { name = "threads1", path = "exercises/20_threads/threads1.rs" }, 161 | { name = "threads1_sol", path = "solutions/20_threads/threads1.rs" }, 162 | { name = "threads2", path = "exercises/20_threads/threads2.rs" }, 163 | { name = "threads2_sol", path = "solutions/20_threads/threads2.rs" }, 164 | { name = "threads3", path = "exercises/20_threads/threads3.rs" }, 165 | { name = "threads3_sol", path = "solutions/20_threads/threads3.rs" }, 166 | { name = "macros1", path = "exercises/21_macros/macros1.rs" }, 167 | { name = "macros1_sol", path = "solutions/21_macros/macros1.rs" }, 168 | { name = "macros2", path = "exercises/21_macros/macros2.rs" }, 169 | { name = "macros2_sol", path = "solutions/21_macros/macros2.rs" }, 170 | { name = "macros3", path = "exercises/21_macros/macros3.rs" }, 171 | { name = "macros3_sol", path = "solutions/21_macros/macros3.rs" }, 172 | { name = "macros4", path = "exercises/21_macros/macros4.rs" }, 173 | { name = "macros4_sol", path = "solutions/21_macros/macros4.rs" }, 174 | { name = "clippy1", path = "exercises/22_clippy/clippy1.rs" }, 175 | { name = "clippy1_sol", path = "solutions/22_clippy/clippy1.rs" }, 176 | { name = "clippy2", path = "exercises/22_clippy/clippy2.rs" }, 177 | { name = "clippy2_sol", path = "solutions/22_clippy/clippy2.rs" }, 178 | { name = "clippy3", path = "exercises/22_clippy/clippy3.rs" }, 179 | { name = "clippy3_sol", path = "solutions/22_clippy/clippy3.rs" }, 180 | { name = "using_as", path = "exercises/23_conversions/using_as.rs" }, 181 | { name = "using_as_sol", path = "solutions/23_conversions/using_as.rs" }, 182 | { name = "from_into", path = "exercises/23_conversions/from_into.rs" }, 183 | { name = "from_into_sol", path = "solutions/23_conversions/from_into.rs" }, 184 | { name = "from_str", path = "exercises/23_conversions/from_str.rs" }, 185 | { name = "from_str_sol", path = "solutions/23_conversions/from_str.rs" }, 186 | { name = "try_from_into", path = "exercises/23_conversions/try_from_into.rs" }, 187 | { name = "try_from_into_sol", path = "solutions/23_conversions/try_from_into.rs" }, 188 | { name = "as_ref_mut", path = "exercises/23_conversions/as_ref_mut.rs" }, 189 | { name = "as_ref_mut_sol", path = "solutions/23_conversions/as_ref_mut.rs" }, 190 | ] 191 | 192 | [package] 193 | name = "exercises" 194 | edition = "2021" 195 | # Don't publish the exercises on crates.io! 196 | publish = false 197 | 198 | [profile.release] 199 | panic = "abort" 200 | 201 | [profile.dev] 202 | panic = "abort" 203 | 204 | [lints.rust] 205 | # You shouldn't write unsafe code in Rustlings! 206 | unsafe_code = "forbid" 207 | # You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust. 208 | unstable_features = "forbid" 209 | # Dead code warnings can't be avoided in some exercises and might distract while learning. 210 | dead_code = "allow" 211 | 212 | [lints.clippy] 213 | # You forgot a `todo!()`! 214 | todo = "forbid" 215 | # This can only happen by mistake in Rustlings. 216 | empty_loop = "forbid" 217 | # No infinite loops are needed in Rustlings. 218 | infinite_loop = "deny" 219 | # You shouldn't leak memory while still learning Rust! 220 | mem_forget = "deny" 221 | # Currently, there are no disallowed methods. This line avoids problems when developing Rustlings. 222 | disallowed_methods = "allow" 223 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # To read 2 | This repository was created by me for the purpose of learning the Rust language. 3 | 4 | >[!IMPORTANT] 5 | > This repository has no meaning for you. 6 | 7 | If you also want to start your way in learning Rust, please follow [this](https://github.com/rust-lang/rustlings) link. -------------------------------------------------------------------------------- /exercises/00_intro/README.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | Rust uses the `print!` and `println!` macros to print text to the console. 4 | 5 | ## Further information 6 | 7 | - [Hello World](https://doc.rust-lang.org/rust-by-example/hello.html) 8 | - [Formatted print](https://doc.rust-lang.org/rust-by-example/hello/print.html) 9 | -------------------------------------------------------------------------------- /exercises/00_intro/intro1.rs: -------------------------------------------------------------------------------- 1 | // TODO: We sometimes encourage you to keep trying things on a given exercise 2 | // even after you already figured it out. If you got everything working and feel 3 | // ready for the next exercise, enter `n` in the terminal. 4 | // 5 | // The exercise file will be reloaded when you change one of the lines below! 6 | // Try adding a new `println!` and check the updated output in the terminal. 7 | 8 | fn main() { 9 | println!(r#" Welcome to... "#); 10 | println!(r#" _ _ _ "#); 11 | println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#); 12 | println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#); 13 | println!(r#" | | | |_| \__ \ |_| | | | | | (_| \__ \ "#); 14 | println!(r#" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ "#); 15 | println!(r#" |___/ "#); 16 | println!(); 17 | println!("This exercise compiles successfully. The remaining exercises contain a compiler"); 18 | println!("or logic error. The central concept behind Rustlings is to fix these errors and"); 19 | println!("solve the exercises. Good luck!"); 20 | println!(); 21 | println!("The file of this exercise is `exercises/00_intro/intro1.rs`. Have a look!"); 22 | println!("The current exercise path will be always shown under the progress bar."); 23 | println!("You can click on the path to open the exercise file in your editor."); 24 | } 25 | -------------------------------------------------------------------------------- /exercises/00_intro/intro2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // TODO: Fix the code to print "Hello world!". 3 | println!("Hello world!"); 4 | } 5 | -------------------------------------------------------------------------------- /exercises/01_variables/README.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | In Rust, variables are immutable by default. 4 | When a variable is immutable, once a value is bound to a name, you can’t change that value. 5 | You can make them mutable by adding `mut` in front of the variable name. 6 | 7 | ## Further information 8 | 9 | - [Variables and Mutability](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html) 10 | -------------------------------------------------------------------------------- /exercises/01_variables/variables1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // TODO: Add the missing keyword. 3 | let x: i32 = 5; 4 | 5 | println!("x has the value {x}"); 6 | } 7 | -------------------------------------------------------------------------------- /exercises/01_variables/variables2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // TODO: Change the line below to fix the compiler error. 3 | let x: i32 = 10; 4 | 5 | if x == 10 { 6 | println!("x is ten!"); 7 | } else { 8 | println!("x is not ten!"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /exercises/01_variables/variables3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // TODO: Change the line below to fix the compiler error. 3 | let x: i32 = 0; 4 | 5 | println!("Number {x}", x=x); 6 | } 7 | -------------------------------------------------------------------------------- /exercises/01_variables/variables4.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the compiler error. 2 | fn main() { 3 | let mut x: i32 = 3; 4 | println!("Number {x}", x=x); 5 | 6 | x = 5; // Don't change this line 7 | println!("Number {x}", x=x); 8 | } 9 | -------------------------------------------------------------------------------- /exercises/01_variables/variables5.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let number = "T-H-R-E-E"; // Don't change this line 3 | println!("Spell a number: {}", number); 4 | 5 | // TODO: Fix the compiler error by changing the line below without renaming the variable. 6 | let number: i32 = 3; 7 | println!("Number plus two is: {}", number + 2); 8 | } 9 | -------------------------------------------------------------------------------- /exercises/01_variables/variables6.rs: -------------------------------------------------------------------------------- 1 | // TODO: Change the line below to fix the compiler error. 2 | const NUMBER: i32 = 3; 3 | 4 | fn main() { 5 | println!("Number: {NUMBER}"); 6 | } 7 | -------------------------------------------------------------------------------- /exercises/02_functions/README.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | Here, you'll learn how to write functions and how the Rust compiler can help you debug errors even 4 | in more complex code. 5 | 6 | ## Further information 7 | 8 | - [How Functions Work](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html) 9 | -------------------------------------------------------------------------------- /exercises/02_functions/functions1.rs: -------------------------------------------------------------------------------- 1 | // TODO: Add some function with the name `call_me` without arguments or a return value. 2 | 3 | fn call_me() {} 4 | 5 | fn main() { 6 | call_me(); // Don't change this line 7 | } 8 | -------------------------------------------------------------------------------- /exercises/02_functions/functions2.rs: -------------------------------------------------------------------------------- 1 | // TODO: Add the missing type of the argument `num` after the colon `:`. 2 | fn call_me(num: u8) { 3 | for i in 0..num { 4 | println!("Ring! Call number {}", i + 1); 5 | } 6 | } 7 | 8 | fn main() { 9 | call_me(3); 10 | } 11 | -------------------------------------------------------------------------------- /exercises/02_functions/functions3.rs: -------------------------------------------------------------------------------- 1 | fn call_me(num: u8) { 2 | for i in 0..num { 3 | println!("Ring! Call number {}", i + 1); 4 | } 5 | } 6 | 7 | fn main() { 8 | // TODO: Fix the function call. 9 | call_me(8); 10 | } 11 | -------------------------------------------------------------------------------- /exercises/02_functions/functions4.rs: -------------------------------------------------------------------------------- 1 | // This store is having a sale where if the price is an even number, you get 10 2 | // Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. 3 | // Don't worry about the function bodies themselves, we are only interested in 4 | // the signatures for now. 5 | 6 | fn is_even(num: i64) -> bool { 7 | num % 2 == 0 8 | } 9 | 10 | // TODO: Fix the function signature. 11 | fn sale_price(price: i64) -> i64 { 12 | if is_even(price) { 13 | price - 10 14 | } else { 15 | price - 3 16 | } 17 | } 18 | 19 | fn main() { 20 | let original_price = 51; 21 | println!("Your sale price is {}", sale_price(original_price)); 22 | } 23 | -------------------------------------------------------------------------------- /exercises/02_functions/functions5.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the function body without changing the signature. 2 | fn square(num: i32) -> i32 { 3 | num * num 4 | } 5 | 6 | fn main() { 7 | let answer = square(3); 8 | println!("The square of 3 is {answer}"); 9 | } 10 | -------------------------------------------------------------------------------- /exercises/03_if/README.md: -------------------------------------------------------------------------------- 1 | # If 2 | 3 | `if`, the most basic (but still surprisingly versatile!) type of control flow, is what you'll learn here. 4 | 5 | ## Further information 6 | 7 | - [Control Flow - if expressions](https://doc.rust-lang.org/book/ch03-05-control-flow.html#if-expressions) 8 | -------------------------------------------------------------------------------- /exercises/03_if/if1.rs: -------------------------------------------------------------------------------- 1 | fn bigger(a: i32, b: i32) -> i32 { 2 | // TODO: Complete this function to return the bigger number! 3 | // If both numbers are equal, any of them can be returned. 4 | // Do not use: 5 | // - another function call 6 | // - additional variables 7 | if a > b { 8 | a 9 | } else if a < b { 10 | b 11 | } else { 12 | a 13 | } 14 | } 15 | 16 | fn main() { 17 | // You can optionally experiment here. 18 | println!("bigger: {}", bigger(1, 2)); 19 | } 20 | 21 | // Don't mind this for now :) 22 | #[cfg(test)] 23 | mod tests { 24 | use super::*; 25 | 26 | #[test] 27 | fn ten_is_bigger_than_eight() { 28 | assert_eq!(10, bigger(10, 8)); 29 | } 30 | 31 | #[test] 32 | fn fortytwo_is_bigger_than_thirtytwo() { 33 | assert_eq!(42, bigger(32, 42)); 34 | } 35 | 36 | #[test] 37 | fn equal_numbers() { 38 | assert_eq!(42, bigger(42, 42)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /exercises/03_if/if2.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the compiler error on this function. 2 | fn picky_eater(food: &str) -> &str { 3 | // I accomplished the task via match because I went a little farther into the course :) 4 | match food { 5 | "strawberry" => "Yummy!", 6 | "potato" => "I guess I can eat that.", 7 | "broccoli" => "No thanks!", 8 | "gummy bears" => "No thanks!", 9 | "literally anything" => "No thanks!", 10 | _ => "No thanks!" 11 | } 12 | } 13 | 14 | fn main() { 15 | // You can optionally experiment here. 16 | } 17 | 18 | // TODO: Read the tests to understand the desired behavior. 19 | // Make all tests pass without changing them. 20 | #[cfg(test)] 21 | mod tests { 22 | use super::*; 23 | 24 | #[test] 25 | fn yummy_food() { 26 | // This means that calling `picky_eater` with the argument "food" should return "Yummy!". 27 | assert_eq!(picky_eater("strawberry"), "Yummy!"); 28 | } 29 | 30 | #[test] 31 | fn neutral_food() { 32 | assert_eq!(picky_eater("potato"), "I guess I can eat that."); 33 | } 34 | 35 | #[test] 36 | fn default_disliked_food() { 37 | assert_eq!(picky_eater("broccoli"), "No thanks!"); 38 | assert_eq!(picky_eater("gummy bears"), "No thanks!"); 39 | assert_eq!(picky_eater("literally anything"), "No thanks!"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /exercises/03_if/if3.rs: -------------------------------------------------------------------------------- 1 | fn animal_habitat(animal: &str) -> &str { 2 | // TODO: Fix the compiler error in the statement below. 3 | let identifier: i32 = if animal == "crab" { 4 | 1 5 | } else if animal == "gopher" { 6 | 2 7 | } else if animal == "snake" { 8 | 3 9 | } else { 10 | 0 11 | }; 12 | 13 | // Don't change the expression below! 14 | if identifier == 1 { 15 | "Beach" 16 | } else if identifier == 2 { 17 | "Burrow" 18 | } else if identifier == 3 { 19 | "Desert" 20 | } else { 21 | "Unknown" 22 | } 23 | } 24 | 25 | fn main() { 26 | // You can optionally experiment here. 27 | } 28 | 29 | // Don't change the tests! 30 | #[cfg(test)] 31 | mod tests { 32 | use super::*; 33 | 34 | #[test] 35 | fn gopher_lives_in_burrow() { 36 | assert_eq!(animal_habitat("gopher"), "Burrow") 37 | } 38 | 39 | #[test] 40 | fn snake_lives_in_desert() { 41 | assert_eq!(animal_habitat("snake"), "Desert") 42 | } 43 | 44 | #[test] 45 | fn crab_lives_on_beach() { 46 | assert_eq!(animal_habitat("crab"), "Beach") 47 | } 48 | 49 | #[test] 50 | fn unknown_animal() { 51 | assert_eq!(animal_habitat("dinosaur"), "Unknown") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /exercises/04_primitive_types/README.md: -------------------------------------------------------------------------------- 1 | # Primitive Types 2 | 3 | Rust has a couple of basic types that are directly implemented into the 4 | compiler. In this section, we'll go through the most important ones. 5 | 6 | ## Further information 7 | 8 | - [Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html) 9 | - [The Slice Type](https://doc.rust-lang.org/book/ch04-03-slices.html) 10 | -------------------------------------------------------------------------------- /exercises/04_primitive_types/primitive_types1.rs: -------------------------------------------------------------------------------- 1 | // Booleans (`bool`) 2 | 3 | fn main() { 4 | let is_morning = true; 5 | if is_morning { 6 | println!("Good morning!"); 7 | } 8 | 9 | // TODO: Define a boolean variable with the name `is_evening` before the `if` statement below. 10 | // The value of the variable should be the negation (opposite) of `is_morning`. 11 | // let … 12 | 13 | let is_evening: bool = !is_morning; 14 | 15 | if is_evening { 16 | println!("Good evening!"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /exercises/04_primitive_types/primitive_types2.rs: -------------------------------------------------------------------------------- 1 | // Characters (`char`) 2 | 3 | fn main() { 4 | // Note the _single_ quotes, these are different from the double quotes 5 | // you've been seeing around. 6 | let my_first_initial = 'C'; 7 | if my_first_initial.is_alphabetic() { 8 | println!("Alphabetical!"); 9 | } else if my_first_initial.is_numeric() { 10 | println!("Numerical!"); 11 | } else { 12 | println!("Neither alphabetic nor numeric!"); 13 | } 14 | 15 | // TODO: Analogous to the example before, declare a variable called `your_character` 16 | // below with your favorite character. 17 | // Try a letter, try a digit (in single quotes), try a special character, try a character 18 | // from a different language than your own, try an emoji 😉 19 | // let your_character = ''; 20 | 21 | let your_character: char = '😉'; 22 | 23 | if your_character.is_alphabetic() { 24 | println!("Alphabetical!"); 25 | } else if your_character.is_numeric() { 26 | println!("Numerical!"); 27 | } else { 28 | println!("Neither alphabetic nor numeric!"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /exercises/04_primitive_types/primitive_types3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // TODO: Create an array called `a` with at least 100 elements in it. 3 | // let a = ??? 4 | 5 | let a: [i32; 100] = [0;100]; 6 | 7 | if a.len() >= 100 { 8 | println!("Wow, that's a big array!"); 9 | } else { 10 | println!("Meh, I eat arrays like that for breakfast."); 11 | panic!("Array not big enough, more elements needed"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /exercises/04_primitive_types/primitive_types4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // You can optionally experiment here. 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | #[test] 8 | fn slice_out_of_array() { 9 | let a = [1, 2, 3, 4, 5]; 10 | 11 | // TODO: Get a slice called `nice_slice` out of the array `a` so that the test passes. 12 | // let nice_slice = ??? 13 | let nice_slice = &a[1..4]; 14 | 15 | assert_eq!([2, 3, 4], nice_slice); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /exercises/04_primitive_types/primitive_types5.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let cat = ("Furry McFurson", 3.5); 3 | 4 | // TODO: Destructure the `cat` tuple in one statement so that the println works. 5 | // let /* your pattern here */ = cat; 6 | 7 | let (name , age) = &cat; 8 | 9 | println!("{name} is {age} years old"); 10 | } 11 | -------------------------------------------------------------------------------- /exercises/04_primitive_types/primitive_types6.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // You can optionally experiment here. 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | #[test] 8 | fn indexing_tuple() { 9 | let numbers = (1, 2, 3); 10 | 11 | // TODO: Use a tuple index to access the second element of `numbers` 12 | // and assign it to a variable called `second`. 13 | // let second = ???; 14 | let second = numbers.1; 15 | 16 | assert_eq!(second, 2, "This is not the 2nd number in the tuple!"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /exercises/05_vecs/README.md: -------------------------------------------------------------------------------- 1 | # Vectors 2 | 3 | Vectors are one of the most-used Rust data structures. In other programming 4 | languages, they'd simply be called Arrays, but since Rust operates on a 5 | bit of a lower level, an array in Rust is stored on the stack (meaning it 6 | can't grow or shrink, and the size needs to be known at compile time), 7 | and a Vector is stored in the heap (where these restrictions do not apply). 8 | 9 | Vectors are a bit of a later chapter in the book, but we think that they're 10 | useful enough to talk about them a bit earlier. We shall be talking about 11 | the other useful data structure, hash maps, later. 12 | 13 | ## Further information 14 | 15 | - [Storing Lists of Values with Vectors](https://doc.rust-lang.org/book/ch08-01-vectors.html) 16 | - [`iter_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut) 17 | - [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map) 18 | -------------------------------------------------------------------------------- /exercises/05_vecs/vecs1.rs: -------------------------------------------------------------------------------- 1 | fn array_and_vec() -> ([i32; 4], Vec) { 2 | let a = [10, 20, 30, 40]; // Array 3 | 4 | // TODO: Create a vector called `v` which contains the exact same elements as in the array `a`. 5 | // Use the vector macro. 6 | // let v = ???; 7 | 8 | let v = a.to_vec(); 9 | (a, v) 10 | } 11 | 12 | fn main() { 13 | // You can optionally experiment here. 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use super::*; 19 | 20 | #[test] 21 | fn test_array_and_vec_similarity() { 22 | let (a, v) = array_and_vec(); 23 | assert_eq!(a, *v); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /exercises/05_vecs/vecs2.rs: -------------------------------------------------------------------------------- 1 | fn vec_loop(input: &[i32]) -> Vec { 2 | let mut output = Vec::new(); 3 | 4 | for element in input { 5 | // TODO: Multiply each element in the `input` slice by 2 and push it to 6 | // the `output` vector. 7 | output.push(element * 2); 8 | } 9 | 10 | output 11 | } 12 | 13 | fn vec_map_example(input: &[i32]) -> Vec { 14 | // An example of collecting a vector after mapping. 15 | // We map each element of the `input` slice to its value plus 1. 16 | // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`. 17 | input.iter().map(|element| element + 1).collect() 18 | } 19 | 20 | fn vec_map(input: &[i32]) -> Vec { 21 | // TODO: Here, we also want to multiply each element in the `input` slice 22 | // by 2, but with iterator mapping instead of manually pushing into an empty 23 | // vector. 24 | // See the example in the function `vec_map_example` above. 25 | input 26 | .iter() 27 | .map(|element| { 28 | element * 2 29 | }) 30 | .collect() 31 | } 32 | 33 | fn main() { 34 | // You can optionally experiment here. 35 | } 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | use super::*; 40 | 41 | #[test] 42 | fn test_vec_loop() { 43 | let input = [2, 4, 6, 8, 10]; 44 | let ans = vec_loop(&input); 45 | assert_eq!(ans, [4, 8, 12, 16, 20]); 46 | } 47 | 48 | #[test] 49 | fn test_vec_map_example() { 50 | let input = [1, 2, 3]; 51 | let ans = vec_map_example(&input); 52 | assert_eq!(ans, [2, 3, 4]); 53 | } 54 | 55 | #[test] 56 | fn test_vec_map() { 57 | let input = [2, 4, 6, 8, 10]; 58 | let ans = vec_map(&input); 59 | assert_eq!(ans, [4, 8, 12, 16, 20]); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /exercises/06_move_semantics/README.md: -------------------------------------------------------------------------------- 1 | # Move Semantics 2 | 3 | These exercises are adapted from [pnkfelix](https://github.com/pnkfelix)'s [Rust Tutorial](https://pnkfelix.github.io/rust-examples-icfp2014/) -- Thank you Felix!!! 4 | 5 | ## Further information 6 | 7 | For this section, the book links are especially important. 8 | 9 | - [Ownership](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html) 10 | - [Reference and borrowing](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html) 11 | -------------------------------------------------------------------------------- /exercises/06_move_semantics/move_semantics1.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the compiler error in this function. 2 | fn fill_vec(vec: Vec) -> Vec { 3 | let mut vec = vec; 4 | 5 | vec.push(88); 6 | 7 | vec 8 | } 9 | 10 | fn main() { 11 | // You can optionally experiment here. 12 | } 13 | 14 | #[cfg(test)] 15 | mod tests { 16 | use super::*; 17 | 18 | #[test] 19 | fn move_semantics1() { 20 | let vec0 = vec![22, 44, 66]; 21 | let vec1 = fill_vec(vec0); 22 | assert_eq!(vec1, vec![22, 44, 66, 88]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /exercises/06_move_semantics/move_semantics2.rs: -------------------------------------------------------------------------------- 1 | fn fill_vec(vec: Vec) -> Vec { 2 | let mut vec = vec; 3 | 4 | vec.push(88); 5 | 6 | vec 7 | } 8 | 9 | fn main() { 10 | // You can optionally experiment here. 11 | } 12 | 13 | #[cfg(test)] 14 | mod tests { 15 | use super::*; 16 | 17 | // TODO: Make both vectors `vec0` and `vec1` accessible at the same time to 18 | // fix the compiler error in the test. 19 | #[test] 20 | fn move_semantics2() { 21 | let vec0 = vec![22, 44, 66]; 22 | 23 | let vec1 = fill_vec(vec0.clone()); 24 | 25 | assert_eq!(vec0, [22, 44, 66]); 26 | assert_eq!(vec1, [22, 44, 66, 88]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /exercises/06_move_semantics/move_semantics3.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the compiler error in the function without adding any new line. 2 | fn fill_vec(mut vec: Vec) -> Vec { 3 | vec.push(88); 4 | 5 | vec 6 | } 7 | 8 | fn main() { 9 | // You can optionally experiment here. 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use super::*; 15 | 16 | #[test] 17 | fn move_semantics3() { 18 | let vec0 = vec![22, 44, 66]; 19 | let vec1 = fill_vec(vec0.clone()); 20 | assert_eq!(vec1, [22, 44, 66, 88]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /exercises/06_move_semantics/move_semantics4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // You can optionally experiment here. 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | // TODO: Fix the compiler errors only by reordering the lines in the test. 8 | // Don't add, change or remove any line. 9 | #[test] 10 | fn move_semantics4() { 11 | let mut x = Vec::new(); 12 | let y = &mut x; 13 | y.push(42); 14 | let z = &mut x; 15 | z.push(13); 16 | assert_eq!(x, [42, 13]); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /exercises/06_move_semantics/move_semantics5.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::ptr_arg)] 2 | 3 | // TODO: Fix the compiler errors without changing anything except adding or 4 | // removing references (the character `&`). 5 | 6 | // Shouldn't take ownership 7 | fn get_char(data: String) -> char { 8 | data.chars().last().unwrap() 9 | } 10 | 11 | // Should take ownership 12 | fn string_uppercase(mut data: String) { 13 | data = data.to_uppercase(); 14 | 15 | println!("{data}"); 16 | } 17 | 18 | fn main() { 19 | let data = "Rust is great!".to_string(); 20 | 21 | get_char(data.clone()); 22 | 23 | string_uppercase(data); 24 | } 25 | -------------------------------------------------------------------------------- /exercises/07_structs/README.md: -------------------------------------------------------------------------------- 1 | # Structs 2 | 3 | Rust has three struct types: a classic C struct, a tuple struct, and a unit struct. 4 | 5 | ## Further information 6 | 7 | - [Structures](https://doc.rust-lang.org/book/ch05-01-defining-structs.html) 8 | - [Method Syntax](https://doc.rust-lang.org/book/ch05-03-method-syntax.html) 9 | -------------------------------------------------------------------------------- /exercises/07_structs/structs1.rs: -------------------------------------------------------------------------------- 1 | struct ColorRegularStruct { 2 | // TODO: Add the fields that the test `regular_structs` expects. 3 | // What types should the fields have? What are the minimum and maximum values for RGB colors? 4 | 5 | red: u8, 6 | green: u8, 7 | blue: u8, 8 | } 9 | 10 | struct ColorTupleStruct(u8, u8, u8); 11 | 12 | #[derive(Debug)] 13 | struct UnitStruct; 14 | 15 | fn main() { 16 | // You can optionally experiment here. 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | 23 | #[test] 24 | fn regular_structs() { 25 | // TODO: Instantiate a regular struct. 26 | // let green = 27 | let green = ColorRegularStruct{ 28 | red: 0, 29 | green: 255, 30 | blue: 0, 31 | }; 32 | 33 | assert_eq!(green.red, 0); 34 | assert_eq!(green.green, 255); 35 | assert_eq!(green.blue, 0); 36 | } 37 | 38 | #[test] 39 | fn tuple_structs() { 40 | // TODO: Instantiate a tuple struct. 41 | // let green = 42 | let green = ColorTupleStruct(0, 255, 0); 43 | 44 | assert_eq!(green.0, 0); 45 | assert_eq!(green.1, 255); 46 | assert_eq!(green.2, 0); 47 | } 48 | 49 | #[test] 50 | fn unit_structs() { 51 | // TODO: Instantiate a unit struct. 52 | // let unit_struct = 53 | let unit_struct = UnitStruct; 54 | let message = format!("{unit_struct:?}s are fun!"); 55 | 56 | assert_eq!(message, "UnitStructs are fun!"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /exercises/07_structs/structs2.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct Order { 3 | name: String, 4 | year: u32, 5 | made_by_phone: bool, 6 | made_by_mobile: bool, 7 | made_by_email: bool, 8 | item_number: u32, 9 | count: u32, 10 | } 11 | 12 | fn create_order_template() -> Order { 13 | Order { 14 | name: String::from("Bob"), 15 | year: 2019, 16 | made_by_phone: false, 17 | made_by_mobile: false, 18 | made_by_email: true, 19 | item_number: 123, 20 | count: 0, 21 | } 22 | } 23 | 24 | fn main() { 25 | // You can optionally experiment here. 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn your_order() { 34 | let order_template = create_order_template(); 35 | 36 | // TODO: Create your own order using the update syntax and template above! 37 | // let your_order = 38 | let mut your_order = create_order_template(); 39 | your_order.name = String::from("Hacker in Rust"); 40 | your_order.count = 1; 41 | 42 | assert_eq!(your_order.name, "Hacker in Rust"); 43 | assert_eq!(your_order.year, order_template.year); 44 | assert_eq!(your_order.made_by_phone, order_template.made_by_phone); 45 | assert_eq!(your_order.made_by_mobile, order_template.made_by_mobile); 46 | assert_eq!(your_order.made_by_email, order_template.made_by_email); 47 | assert_eq!(your_order.item_number, order_template.item_number); 48 | assert_eq!(your_order.count, 1); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /exercises/07_structs/structs3.rs: -------------------------------------------------------------------------------- 1 | // Structs contain data, but can also have logic. In this exercise, we have 2 | // defined the `Package` struct, and we want to test some logic attached to it. 3 | 4 | #[derive(Debug)] 5 | struct Package { 6 | sender_country: String, 7 | recipient_country: String, 8 | weight_in_grams: u32, 9 | } 10 | 11 | impl Package { 12 | fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self { 13 | if weight_in_grams < 10 { 14 | // This isn't how you should handle errors in Rust, but we will 15 | // learn about error handling later. 16 | panic!("Can't ship a package with weight below 10 grams"); 17 | } 18 | 19 | Self { 20 | sender_country, 21 | recipient_country, 22 | weight_in_grams, 23 | } 24 | } 25 | 26 | // TODO: Add the correct return type to the function signature. 27 | fn is_international(&self) -> bool { 28 | // TODO: Read the tests that use this method to find out when a package 29 | // is considered international. 30 | self.sender_country != self.recipient_country 31 | } 32 | 33 | // TODO: Add the correct return type to the function signature. 34 | fn get_fees(&self, cents_per_gram: u32) -> u32 { 35 | // TODO: Calculate the package's fees. 36 | cents_per_gram * self.weight_in_grams 37 | } 38 | } 39 | 40 | fn main() { 41 | // You can optionally experiment here. 42 | } 43 | 44 | #[cfg(test)] 45 | mod tests { 46 | use super::*; 47 | 48 | #[test] 49 | #[should_panic] 50 | fn fail_creating_weightless_package() { 51 | let sender_country = String::from("Spain"); 52 | let recipient_country = String::from("Austria"); 53 | 54 | Package::new(sender_country, recipient_country, 5); 55 | } 56 | 57 | #[test] 58 | fn create_international_package() { 59 | let sender_country = String::from("Spain"); 60 | let recipient_country = String::from("Russia"); 61 | 62 | let package = Package::new(sender_country, recipient_country, 1200); 63 | 64 | assert!(package.is_international()); 65 | } 66 | 67 | #[test] 68 | fn create_local_package() { 69 | let sender_country = String::from("Canada"); 70 | let recipient_country = sender_country.clone(); 71 | 72 | let package = Package::new(sender_country, recipient_country, 1200); 73 | 74 | assert!(!package.is_international()); 75 | } 76 | 77 | #[test] 78 | fn calculate_transport_fees() { 79 | let sender_country = String::from("Spain"); 80 | let recipient_country = String::from("Spain"); 81 | 82 | let cents_per_gram = 3; 83 | 84 | let package = Package::new(sender_country, recipient_country, 1500); 85 | 86 | assert_eq!(package.get_fees(cents_per_gram), 4500); 87 | assert_eq!(package.get_fees(cents_per_gram * 2), 9000); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /exercises/08_enums/README.md: -------------------------------------------------------------------------------- 1 | # Enums 2 | 3 | Rust allows you to define types called "enums" which enumerate possible values. 4 | Enums are a feature in many languages, but their capabilities differ in each language. Rust’s enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell. 5 | Useful in combination with enums is Rust's "pattern matching" facility, which makes it easy to run different code for different values of an enumeration. 6 | 7 | ## Further information 8 | 9 | - [Enums](https://doc.rust-lang.org/book/ch06-00-enums.html) 10 | - [Pattern syntax](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html) 11 | -------------------------------------------------------------------------------- /exercises/08_enums/enums1.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | enum Message { 3 | // TODO: Define a few types of messages as used below. 4 | Resize, 5 | Move, 6 | Echo, 7 | ChangeColor, 8 | Quit, 9 | } 10 | 11 | fn main() { 12 | println!("{:?}", Message::Resize); 13 | println!("{:?}", Message::Move); 14 | println!("{:?}", Message::Echo); 15 | println!("{:?}", Message::ChangeColor); 16 | println!("{:?}", Message::Quit); 17 | } 18 | -------------------------------------------------------------------------------- /exercises/08_enums/enums2.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct Point { 3 | x: u64, 4 | y: u64, 5 | } 6 | 7 | #[derive(Debug)] 8 | enum Message { 9 | // TODO: Define the different variants used below. 10 | Resize { width: u8, height: u8 }, 11 | Move(Point), 12 | Echo(String), 13 | ChangeColor(u8, u8, u8), 14 | Quit, 15 | } 16 | 17 | impl Message { 18 | fn call(&self) { 19 | println!("{self:?}"); 20 | } 21 | } 22 | 23 | fn main() { 24 | let messages = [ 25 | Message::Resize { 26 | width: 10, 27 | height: 30, 28 | }, 29 | Message::Move(Point { x: 10, y: 15 }), 30 | Message::Echo(String::from("hello world")), 31 | Message::ChangeColor(200, 255, 255), 32 | Message::Quit, 33 | ]; 34 | 35 | for message in &messages { 36 | message.call(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /exercises/08_enums/enums3.rs: -------------------------------------------------------------------------------- 1 | struct Point { 2 | x: u64, 3 | y: u64, 4 | } 5 | 6 | enum Message { 7 | Resize { width: u64, height: u64 }, 8 | Move(Point), 9 | Echo(String), 10 | ChangeColor(u8, u8, u8), 11 | Quit, 12 | } 13 | 14 | struct State { 15 | width: u64, 16 | height: u64, 17 | position: Point, 18 | message: String, 19 | // RGB color composed of red, green and blue. 20 | color: (u8, u8, u8), 21 | quit: bool, 22 | } 23 | 24 | impl State { 25 | fn resize(&mut self, width: u64, height: u64) { 26 | self.width = width; 27 | self.height = height; 28 | } 29 | 30 | fn move_position(&mut self, point: Point) { 31 | self.position = point; 32 | } 33 | 34 | fn echo(&mut self, s: String) { 35 | self.message = s; 36 | } 37 | 38 | fn change_color(&mut self, red: u8, green: u8, blue: u8) { 39 | self.color = (red, green, blue); 40 | } 41 | 42 | fn quit(&mut self) { 43 | self.quit = true; 44 | } 45 | 46 | fn process(&mut self, message: Message) { 47 | // TODO: Create a match expression to process the different message 48 | // variants using the methods defined above. 49 | match message { 50 | Message::Resize { width, height} => { self.resize(width, height) } 51 | Message::Move(point) => { self.move_position(point) } 52 | Message::Echo(s) => { self.echo(s) } 53 | Message::ChangeColor(r, g, b) => {self.change_color(r, g, b);} 54 | Message::Quit => { self.quit() } 55 | } 56 | } 57 | } 58 | 59 | fn main() { 60 | // You can optionally experiment here. 61 | } 62 | 63 | #[cfg(test)] 64 | mod tests { 65 | use super::*; 66 | 67 | #[test] 68 | fn test_match_message_call() { 69 | let mut state = State { 70 | width: 0, 71 | height: 0, 72 | position: Point { x: 0, y: 0 }, 73 | message: String::from("hello world"), 74 | color: (0, 0, 0), 75 | quit: false, 76 | }; 77 | 78 | state.process(Message::Resize { 79 | width: 10, 80 | height: 30, 81 | }); 82 | state.process(Message::Move(Point { x: 10, y: 15 })); 83 | state.process(Message::Echo(String::from("Hello world!"))); 84 | state.process(Message::ChangeColor(255, 0, 255)); 85 | state.process(Message::Quit); 86 | 87 | assert_eq!(state.width, 10); 88 | assert_eq!(state.height, 30); 89 | assert_eq!(state.position.x, 10); 90 | assert_eq!(state.position.y, 15); 91 | assert_eq!(state.message, "Hello world!"); 92 | assert_eq!(state.color, (255, 0, 255)); 93 | assert!(state.quit); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /exercises/09_strings/README.md: -------------------------------------------------------------------------------- 1 | # Strings 2 | 3 | Rust has two string types, a string slice (`&str`) and an owned string (`String`). 4 | We're not going to dictate when you should use which one, but we'll show you how 5 | to identify and create them, as well as use them. 6 | 7 | ## Further information 8 | 9 | - [Strings](https://doc.rust-lang.org/book/ch08-02-strings.html) 10 | -------------------------------------------------------------------------------- /exercises/09_strings/strings1.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the compiler error without changing the function signature. 2 | fn current_favorite_color() -> String { 3 | String::from("blue") 4 | } 5 | 6 | fn main() { 7 | let answer = current_favorite_color(); 8 | println!("My current favorite color is {answer}"); 9 | } 10 | -------------------------------------------------------------------------------- /exercises/09_strings/strings2.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the compiler error in the `main` function without changing this function. 2 | fn is_a_color_word(attempt: &str) -> bool { 3 | attempt == "green" || attempt == "blue" || attempt == "red" 4 | } 5 | 6 | fn main() { 7 | let word = String::from("green"); // Don't change this line. 8 | 9 | if is_a_color_word(&word) { 10 | println!("That is a color word I know!"); 11 | } else { 12 | println!("That is not a color word I know."); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /exercises/09_strings/strings3.rs: -------------------------------------------------------------------------------- 1 | fn trim_me(input: &str) -> &str { 2 | // TODO: Remove whitespace from both ends of a string. 3 | input.trim() 4 | } 5 | 6 | fn compose_me(input: &str) -> String { 7 | // TODO: Add " world!" to the string! There are multiple ways to do this. 8 | input.to_string() + " world!" 9 | } 10 | 11 | fn replace_me(input: &str) -> String { 12 | // TODO: Replace "cars" in the string with "balloons". 13 | input.replace("cars", "balloons") 14 | } 15 | 16 | fn main() { 17 | // You can optionally experiment here. 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use super::*; 23 | 24 | #[test] 25 | fn trim_a_string() { 26 | assert_eq!(trim_me("Hello! "), "Hello!"); 27 | assert_eq!(trim_me(" What's up!"), "What's up!"); 28 | assert_eq!(trim_me(" Hola! "), "Hola!"); 29 | } 30 | 31 | #[test] 32 | fn compose_a_string() { 33 | assert_eq!(compose_me("Hello"), "Hello world!"); 34 | assert_eq!(compose_me("Goodbye"), "Goodbye world!"); 35 | } 36 | 37 | #[test] 38 | fn replace_a_string() { 39 | assert_eq!( 40 | replace_me("I think cars are cool"), 41 | "I think balloons are cool", 42 | ); 43 | assert_eq!( 44 | replace_me("I love to look at cars"), 45 | "I love to look at balloons", 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /exercises/09_strings/strings4.rs: -------------------------------------------------------------------------------- 1 | // Calls of this function should be replaced with calls of `string_slice` or `string`. 2 | fn placeholder() {} 3 | 4 | fn string_slice(arg: &str) { 5 | println!("{arg}"); 6 | } 7 | 8 | fn string(arg: String) { 9 | println!("{arg}"); 10 | } 11 | 12 | // TODO: Here are a bunch of values - some are `String`, some are `&str`. 13 | // Your task is to replace `placeholder(…)` with either `string_slice(…)` 14 | // or `string(…)` depending on what you think each value is. 15 | fn main() { 16 | string_slice("blue"); 17 | 18 | string("red".to_string()); 19 | 20 | string(String::from("hi")); 21 | 22 | string("rust is fun!".to_owned()); 23 | 24 | string_slice("nice weather".into()); 25 | 26 | string(format!("Interpolation {}", "Station")); 27 | 28 | // WARNING: This is byte indexing, not character indexing. 29 | // Character indexing can be done using `s.chars().nth(INDEX)`. 30 | string_slice(&String::from("abc")[0..1]); 31 | 32 | string_slice(" hello there ".trim()); 33 | 34 | string("Happy Monday!".replace("Mon", "Tues")); 35 | 36 | string("mY sHiFt KeY iS sTiCkY".to_lowercase()); 37 | } 38 | -------------------------------------------------------------------------------- /exercises/10_modules/README.md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | In this section we'll give you an introduction to Rust's module system. 4 | 5 | ## Further information 6 | 7 | - [The Module System](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html) 8 | -------------------------------------------------------------------------------- /exercises/10_modules/modules1.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the compiler error about calling a private function. 2 | mod sausage_factory { 3 | // Don't let anybody outside of this module see this! 4 | fn get_secret_recipe() -> String { 5 | String::from("Ginger") 6 | } 7 | 8 | fn make_sausage() { 9 | get_secret_recipe(); 10 | println!("sausage!"); 11 | } 12 | } 13 | 14 | fn main() { 15 | sausage_factory::make_sausage(); 16 | } 17 | -------------------------------------------------------------------------------- /exercises/10_modules/modules2.rs: -------------------------------------------------------------------------------- 1 | // You can bring module paths into scopes and provide new names for them with 2 | // the `use` and `as` keywords. 3 | 4 | mod delicious_snacks { 5 | // TODO: Add the following two `use` statements after fixing them. 6 | // use self::fruits::PEAR as ???; 7 | // use self::veggies::CUCUMBER as ???; 8 | 9 | mod fruits { 10 | pub const PEAR: &str = "Pear"; 11 | pub const APPLE: &str = "Apple"; 12 | } 13 | 14 | mod veggies { 15 | pub const CUCUMBER: &str = "Cucumber"; 16 | pub const CARROT: &str = "Carrot"; 17 | } 18 | } 19 | 20 | fn main() { 21 | println!( 22 | "favorite snacks: {} and {}", 23 | delicious_snacks::fruit, 24 | delicious_snacks::veggie, 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /exercises/10_modules/modules3.rs: -------------------------------------------------------------------------------- 1 | // You can use the `use` keyword to bring module paths from modules from 2 | // anywhere and especially from the standard library into your scope. 3 | 4 | // TODO: Bring `SystemTime` and `UNIX_EPOCH` from the `std::time` module into 5 | // your scope. Bonus style points if you can do it with one line! 6 | // use ???; 7 | 8 | fn main() { 9 | match SystemTime::now().duration_since(UNIX_EPOCH) { 10 | Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), 11 | Err(_) => panic!("SystemTime before UNIX EPOCH!"), 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /exercises/11_hashmaps/README.md: -------------------------------------------------------------------------------- 1 | # Hashmaps 2 | 3 | A *hash map* allows you to associate a value with a particular key. 4 | You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map), 5 | [*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages. 6 | 7 | This is the other data structure that we've been talking about before, when 8 | talking about Vecs. 9 | 10 | ## Further information 11 | 12 | - [Storing Keys with Associated Values in Hash Maps](https://doc.rust-lang.org/book/ch08-03-hash-maps.html) 13 | -------------------------------------------------------------------------------- /exercises/11_hashmaps/hashmaps1.rs: -------------------------------------------------------------------------------- 1 | // A basket of fruits in the form of a hash map needs to be defined. The key 2 | // represents the name of the fruit and the value represents how many of that 3 | // particular fruit is in the basket. You have to put at least 3 different 4 | // types of fruits (e.g. apple, banana, mango) in the basket and the total count 5 | // of all the fruits should be at least 5. 6 | 7 | use std::collections::HashMap; 8 | 9 | fn fruit_basket() -> HashMap { 10 | // TODO: Declare the hash map. 11 | // let mut basket = 12 | 13 | // Two bananas are already given for you :) 14 | basket.insert(String::from("banana"), 2); 15 | 16 | // TODO: Put more fruits in your basket. 17 | 18 | basket 19 | } 20 | 21 | fn main() { 22 | // You can optionally experiment here. 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use super::*; 28 | 29 | #[test] 30 | fn at_least_three_types_of_fruits() { 31 | let basket = fruit_basket(); 32 | assert!(basket.len() >= 3); 33 | } 34 | 35 | #[test] 36 | fn at_least_five_fruits() { 37 | let basket = fruit_basket(); 38 | assert!(basket.values().sum::() >= 5); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /exercises/11_hashmaps/hashmaps2.rs: -------------------------------------------------------------------------------- 1 | // We're collecting different fruits to bake a delicious fruit cake. For this, 2 | // we have a basket, which we'll represent in the form of a hash map. The key 3 | // represents the name of each fruit we collect and the value represents how 4 | // many of that particular fruit we have collected. Three types of fruits - 5 | // Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You 6 | // must add fruit to the basket so that there is at least one of each kind and 7 | // more than 11 in total - we have a lot of mouths to feed. You are not allowed 8 | // to insert any more of the fruits that are already in the basket (Apple, 9 | // Mango, and Lychee). 10 | 11 | use std::collections::HashMap; 12 | 13 | #[derive(Hash, PartialEq, Eq, Debug)] 14 | enum Fruit { 15 | Apple, 16 | Banana, 17 | Mango, 18 | Lychee, 19 | Pineapple, 20 | } 21 | 22 | fn fruit_basket(basket: &mut HashMap) { 23 | let fruit_kinds = [ 24 | Fruit::Apple, 25 | Fruit::Banana, 26 | Fruit::Mango, 27 | Fruit::Lychee, 28 | Fruit::Pineapple, 29 | ]; 30 | 31 | for fruit in fruit_kinds { 32 | // TODO: Insert new fruits if they are not already present in the 33 | // basket. Note that you are not allowed to put any type of fruit that's 34 | // already present! 35 | } 36 | } 37 | 38 | fn main() { 39 | // You can optionally experiment here. 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use super::*; 45 | 46 | // Don't modify this function! 47 | fn get_fruit_basket() -> HashMap { 48 | let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)]; 49 | HashMap::from_iter(content) 50 | } 51 | 52 | #[test] 53 | fn test_given_fruits_are_not_modified() { 54 | let mut basket = get_fruit_basket(); 55 | fruit_basket(&mut basket); 56 | assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4); 57 | assert_eq!(*basket.get(&Fruit::Mango).unwrap(), 2); 58 | assert_eq!(*basket.get(&Fruit::Lychee).unwrap(), 5); 59 | } 60 | 61 | #[test] 62 | fn at_least_five_types_of_fruits() { 63 | let mut basket = get_fruit_basket(); 64 | fruit_basket(&mut basket); 65 | let count_fruit_kinds = basket.len(); 66 | assert!(count_fruit_kinds >= 5); 67 | } 68 | 69 | #[test] 70 | fn greater_than_eleven_fruits() { 71 | let mut basket = get_fruit_basket(); 72 | fruit_basket(&mut basket); 73 | let count = basket.values().sum::(); 74 | assert!(count > 11); 75 | } 76 | 77 | #[test] 78 | fn all_fruit_types_in_basket() { 79 | let fruit_kinds = [ 80 | Fruit::Apple, 81 | Fruit::Banana, 82 | Fruit::Mango, 83 | Fruit::Lychee, 84 | Fruit::Pineapple, 85 | ]; 86 | 87 | let mut basket = get_fruit_basket(); 88 | fruit_basket(&mut basket); 89 | 90 | for fruit_kind in fruit_kinds { 91 | let Some(amount) = basket.get(&fruit_kind) else { 92 | panic!("Fruit kind {fruit_kind:?} was not found in basket"); 93 | }; 94 | assert!(*amount > 0); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /exercises/11_hashmaps/hashmaps3.rs: -------------------------------------------------------------------------------- 1 | // A list of scores (one per line) of a soccer match is given. Each line is of 2 | // the form ",,," 3 | // Example: "England,France,4,2" (England scored 4 goals, France 2). 4 | // 5 | // You have to build a scores table containing the name of the team, the total 6 | // number of goals the team scored, and the total number of goals the team 7 | // conceded. 8 | 9 | use std::collections::HashMap; 10 | 11 | // A structure to store the goal details of a team. 12 | #[derive(Default)] 13 | struct TeamScores { 14 | goals_scored: u8, 15 | goals_conceded: u8, 16 | } 17 | 18 | fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> { 19 | // The name of the team is the key and its associated struct is the value. 20 | let mut scores = HashMap::<&str, TeamScores>::new(); 21 | 22 | for line in results.lines() { 23 | let mut split_iterator = line.split(','); 24 | // NOTE: We use `unwrap` because we didn't deal with error handling yet. 25 | let team_1_name = split_iterator.next().unwrap(); 26 | let team_2_name = split_iterator.next().unwrap(); 27 | let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap(); 28 | let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); 29 | 30 | // TODO: Populate the scores table with the extracted details. 31 | // Keep in mind that goals scored by team 1 will be the number of goals 32 | // conceded by team 2. Similarly, goals scored by team 2 will be the 33 | // number of goals conceded by team 1. 34 | } 35 | 36 | scores 37 | } 38 | 39 | fn main() { 40 | // You can optionally experiment here. 41 | } 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | use super::*; 46 | 47 | const RESULTS: &str = "England,France,4,2 48 | France,Italy,3,1 49 | Poland,Spain,2,0 50 | Germany,England,2,1 51 | England,Spain,1,0"; 52 | 53 | #[test] 54 | fn build_scores() { 55 | let scores = build_scores_table(RESULTS); 56 | 57 | assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"] 58 | .into_iter() 59 | .all(|team_name| scores.contains_key(team_name))); 60 | } 61 | 62 | #[test] 63 | fn validate_team_score_1() { 64 | let scores = build_scores_table(RESULTS); 65 | let team = scores.get("England").unwrap(); 66 | assert_eq!(team.goals_scored, 6); 67 | assert_eq!(team.goals_conceded, 4); 68 | } 69 | 70 | #[test] 71 | fn validate_team_score_2() { 72 | let scores = build_scores_table(RESULTS); 73 | let team = scores.get("Spain").unwrap(); 74 | assert_eq!(team.goals_scored, 0); 75 | assert_eq!(team.goals_conceded, 3); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /exercises/12_options/README.md: -------------------------------------------------------------------------------- 1 | # Options 2 | 3 | Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not. 4 | Option types are very common in Rust code, as they have a number of uses: 5 | 6 | - Initial values 7 | - Return values for functions that are not defined over their entire input range (partial functions) 8 | - Return value for otherwise reporting simple errors, where None is returned on error 9 | - Optional struct fields 10 | - Struct fields that can be loaned or "taken" 11 | - Optional function arguments 12 | - Nullable pointers 13 | - Swapping things out of difficult situations 14 | 15 | ## Further Information 16 | 17 | - [Option Enum Format](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-enum-definitions) 18 | - [Option Module Documentation](https://doc.rust-lang.org/std/option/) 19 | - [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html) 20 | - [if let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html) 21 | - [while let](https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html) 22 | -------------------------------------------------------------------------------- /exercises/12_options/options1.rs: -------------------------------------------------------------------------------- 1 | // This function returns how much icecream there is left in the fridge. 2 | // If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, 3 | // someone eats it all, so no icecream is left (value 0). Return `None` if 4 | // `hour_of_day` is higher than 23. 5 | fn maybe_icecream(hour_of_day: u16) -> Option { 6 | // TODO: Complete the function body. 7 | } 8 | 9 | fn main() { 10 | // You can optionally experiment here. 11 | } 12 | 13 | #[cfg(test)] 14 | mod tests { 15 | use super::*; 16 | 17 | #[test] 18 | fn raw_value() { 19 | // TODO: Fix this test. How do you get the value contained in the 20 | // Option? 21 | let icecreams = maybe_icecream(12); 22 | 23 | assert_eq!(icecreams, 5); // Don't change this line. 24 | } 25 | 26 | #[test] 27 | fn check_icecream() { 28 | assert_eq!(maybe_icecream(0), Some(5)); 29 | assert_eq!(maybe_icecream(9), Some(5)); 30 | assert_eq!(maybe_icecream(18), Some(5)); 31 | assert_eq!(maybe_icecream(22), Some(0)); 32 | assert_eq!(maybe_icecream(23), Some(0)); 33 | assert_eq!(maybe_icecream(24), None); 34 | assert_eq!(maybe_icecream(25), None); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /exercises/12_options/options2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // You can optionally experiment here. 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | #[test] 8 | fn simple_option() { 9 | let target = "rustlings"; 10 | let optional_target = Some(target); 11 | 12 | // TODO: Make this an if-let statement whose value is `Some`. 13 | word = optional_target { 14 | assert_eq!(word, target); 15 | } 16 | } 17 | 18 | #[test] 19 | fn layered_option() { 20 | let range = 10; 21 | let mut optional_integers: Vec> = vec![None]; 22 | 23 | for i in 1..=range { 24 | optional_integers.push(Some(i)); 25 | } 26 | 27 | let mut cursor = range; 28 | 29 | // TODO: Make this a while-let statement. Remember that `Vec::pop()` 30 | // adds another layer of `Option`. You can do nested pattern matching 31 | // in if-let and while-let statements. 32 | integer = optional_integers.pop() { 33 | assert_eq!(integer, cursor); 34 | cursor -= 1; 35 | } 36 | 37 | assert_eq!(cursor, 0); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /exercises/12_options/options3.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct Point { 3 | x: i32, 4 | y: i32, 5 | } 6 | 7 | fn main() { 8 | let optional_point = Some(Point { x: 100, y: 200 }); 9 | 10 | // TODO: Fix the compiler error by adding something to this match statement. 11 | match optional_point { 12 | Some(p) => println!("Co-ordinates are {},{}", p.x, p.y), 13 | _ => panic!("No match!"), 14 | } 15 | 16 | println!("{optional_point:?}"); // Don't change this line. 17 | } 18 | -------------------------------------------------------------------------------- /exercises/13_error_handling/README.md: -------------------------------------------------------------------------------- 1 | # Error handling 2 | 3 | Most errors aren’t serious enough to require the program to stop entirely. 4 | Sometimes, when a function fails, it’s for a reason that you can easily interpret and respond to. 5 | For example, if you try to open a file and that operation fails because the file doesn’t exist, you might want to create the file instead of terminating the process. 6 | 7 | ## Further information 8 | 9 | - [Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html) 10 | - [Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html) 11 | - [Result](https://doc.rust-lang.org/rust-by-example/error/result.html) 12 | - [Boxing errors](https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/boxing_errors.html) 13 | -------------------------------------------------------------------------------- /exercises/13_error_handling/errors1.rs: -------------------------------------------------------------------------------- 1 | // TODO: This function refuses to generate text to be printed on a nametag if 2 | // you pass it an empty string. It'd be nicer if it explained what the problem 3 | // was instead of just returning `None`. Thankfully, Rust has a similar 4 | // construct to `Option` that can be used to express error conditions. Change 5 | // the function signature and body to return `Result` instead 6 | // of `Option`. 7 | fn generate_nametag_text(name: String) -> Option { 8 | if name.is_empty() { 9 | // Empty names aren't allowed 10 | None 11 | } else { 12 | Some(format!("Hi! My name is {name}")) 13 | } 14 | } 15 | 16 | fn main() { 17 | // You can optionally experiment here. 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use super::*; 23 | 24 | #[test] 25 | fn generates_nametag_text_for_a_nonempty_name() { 26 | assert_eq!( 27 | generate_nametag_text("Beyoncé".to_string()).as_deref(), 28 | Ok("Hi! My name is Beyoncé"), 29 | ); 30 | } 31 | 32 | #[test] 33 | fn explains_why_generating_nametag_text_fails() { 34 | assert_eq!( 35 | generate_nametag_text(String::new()) 36 | .as_ref() 37 | .map_err(|e| e.as_str()), 38 | Err("Empty names aren't allowed"), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /exercises/13_error_handling/errors2.rs: -------------------------------------------------------------------------------- 1 | // Say we're writing a game where you can buy items with tokens. All items cost 2 | // 5 tokens, and whenever you purchase items there is a processing fee of 1 3 | // token. A player of the game will type in how many items they want to buy, and 4 | // the `total_cost` function will calculate the total cost of the items. Since 5 | // the player typed in the quantity, we get it as a string. They might have 6 | // typed anything, not just numbers! 7 | // 8 | // Right now, this function isn't handling the error case at all. What we want 9 | // to do is: If we call the `total_cost` function on a string that is not a 10 | // number, that function will return a `ParseIntError`. In that case, we want to 11 | // immediately return that error from our function and not try to multiply and 12 | // add. 13 | // 14 | // There are at least two ways to implement this that are both correct. But one 15 | // is a lot shorter! 16 | 17 | use std::num::ParseIntError; 18 | 19 | fn total_cost(item_quantity: &str) -> Result { 20 | let processing_fee = 1; 21 | let cost_per_item = 5; 22 | 23 | // TODO: Handle the error case as described above. 24 | let qty = item_quantity.parse::(); 25 | 26 | Ok(qty * cost_per_item + processing_fee) 27 | } 28 | 29 | fn main() { 30 | // You can optionally experiment here. 31 | } 32 | 33 | #[cfg(test)] 34 | mod tests { 35 | use super::*; 36 | use std::num::IntErrorKind; 37 | 38 | #[test] 39 | fn item_quantity_is_a_valid_number() { 40 | assert_eq!(total_cost("34"), Ok(171)); 41 | } 42 | 43 | #[test] 44 | fn item_quantity_is_an_invalid_number() { 45 | assert_eq!( 46 | total_cost("beep boop").unwrap_err().kind(), 47 | &IntErrorKind::InvalidDigit, 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /exercises/13_error_handling/errors3.rs: -------------------------------------------------------------------------------- 1 | // This is a program that is trying to use a completed version of the 2 | // `total_cost` function from the previous exercise. It's not working though! 3 | // Why not? What should we do to fix it? 4 | 5 | use std::num::ParseIntError; 6 | 7 | // Don't change this function. 8 | fn total_cost(item_quantity: &str) -> Result { 9 | let processing_fee = 1; 10 | let cost_per_item = 5; 11 | let qty = item_quantity.parse::()?; 12 | 13 | Ok(qty * cost_per_item + processing_fee) 14 | } 15 | 16 | // TODO: Fix the compiler error by changing the signature and body of the 17 | // `main` function. 18 | fn main() { 19 | let mut tokens = 100; 20 | let pretend_user_input = "8"; 21 | 22 | // Don't change this line. 23 | let cost = total_cost(pretend_user_input)?; 24 | 25 | if cost > tokens { 26 | println!("You can't afford that many!"); 27 | } else { 28 | tokens -= cost; 29 | println!("You now have {tokens} tokens."); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /exercises/13_error_handling/errors4.rs: -------------------------------------------------------------------------------- 1 | #[derive(PartialEq, Debug)] 2 | enum CreationError { 3 | Negative, 4 | Zero, 5 | } 6 | 7 | #[derive(PartialEq, Debug)] 8 | struct PositiveNonzeroInteger(u64); 9 | 10 | impl PositiveNonzeroInteger { 11 | fn new(value: i64) -> Result { 12 | // TODO: This function shouldn't always return an `Ok`. 13 | Ok(Self(value as u64)) 14 | } 15 | } 16 | 17 | fn main() { 18 | // You can optionally experiment here. 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use super::*; 24 | 25 | #[test] 26 | fn test_creation() { 27 | assert_eq!( 28 | PositiveNonzeroInteger::new(10), 29 | Ok(PositiveNonzeroInteger(10)), 30 | ); 31 | assert_eq!( 32 | PositiveNonzeroInteger::new(-10), 33 | Err(CreationError::Negative), 34 | ); 35 | assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /exercises/13_error_handling/errors5.rs: -------------------------------------------------------------------------------- 1 | // This exercise is an altered version of the `errors4` exercise. It uses some 2 | // concepts that we won't get to until later in the course, like `Box` and the 3 | // `From` trait. It's not important to understand them in detail right now, but 4 | // you can read ahead if you like. For now, think of the `Box` type as 5 | // an "I want anything that does ???" type. 6 | // 7 | // In short, this particular use case for boxes is for when you want to own a 8 | // value and you care only that it is a type which implements a particular 9 | // trait. To do so, The `Box` is declared as of type `Box` where 10 | // `Trait` is the trait the compiler looks for on any value used in that 11 | // context. For this exercise, that context is the potential errors which 12 | // can be returned in a `Result`. 13 | 14 | use std::error::Error; 15 | use std::fmt; 16 | 17 | #[derive(PartialEq, Debug)] 18 | enum CreationError { 19 | Negative, 20 | Zero, 21 | } 22 | 23 | // This is required so that `CreationError` can implement `Error`. 24 | impl fmt::Display for CreationError { 25 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 26 | let description = match *self { 27 | CreationError::Negative => "number is negative", 28 | CreationError::Zero => "number is zero", 29 | }; 30 | f.write_str(description) 31 | } 32 | } 33 | 34 | impl Error for CreationError {} 35 | 36 | #[derive(PartialEq, Debug)] 37 | struct PositiveNonzeroInteger(u64); 38 | 39 | impl PositiveNonzeroInteger { 40 | fn new(value: i64) -> Result { 41 | match value { 42 | x if x < 0 => Err(CreationError::Negative), 43 | 0 => Err(CreationError::Zero), 44 | x => Ok(PositiveNonzeroInteger(x as u64)), 45 | } 46 | } 47 | } 48 | 49 | // TODO: Add the correct return type `Result<(), Box>`. What can we 50 | // use to describe both errors? Is there a trait which both errors implement? 51 | fn main() { 52 | let pretend_user_input = "42"; 53 | let x: i64 = pretend_user_input.parse()?; 54 | println!("output={:?}", PositiveNonzeroInteger::new(x)?); 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /exercises/13_error_handling/errors6.rs: -------------------------------------------------------------------------------- 1 | // Using catch-all error types like `Box` isn't recommended for 2 | // library code where callers might want to make decisions based on the error 3 | // content instead of printing it out or propagating it further. Here, we define 4 | // a custom error type to make it possible for callers to decide what to do next 5 | // when our function returns an error. 6 | 7 | use std::num::ParseIntError; 8 | 9 | #[derive(PartialEq, Debug)] 10 | enum CreationError { 11 | Negative, 12 | Zero, 13 | } 14 | 15 | // A custom error type that we will be using in `PositiveNonzeroInteger::parse`. 16 | #[derive(PartialEq, Debug)] 17 | enum ParsePosNonzeroError { 18 | Creation(CreationError), 19 | ParseInt(ParseIntError), 20 | } 21 | 22 | impl ParsePosNonzeroError { 23 | fn from_creation(err: CreationError) -> Self { 24 | Self::Creation(err) 25 | } 26 | 27 | // TODO: Add another error conversion function here. 28 | // fn from_parse_int(???) -> Self { ??? } 29 | } 30 | 31 | #[derive(PartialEq, Debug)] 32 | struct PositiveNonzeroInteger(u64); 33 | 34 | impl PositiveNonzeroInteger { 35 | fn new(value: i64) -> Result { 36 | match value { 37 | x if x < 0 => Err(CreationError::Negative), 38 | 0 => Err(CreationError::Zero), 39 | x => Ok(Self(x as u64)), 40 | } 41 | } 42 | 43 | fn parse(s: &str) -> Result { 44 | // TODO: change this to return an appropriate error instead of panicking 45 | // when `parse()` returns an error. 46 | let x: i64 = s.parse().unwrap(); 47 | Self::new(x).map_err(ParsePosNonzeroError::from_creation) 48 | } 49 | } 50 | 51 | fn main() { 52 | // You can optionally experiment here. 53 | } 54 | 55 | #[cfg(test)] 56 | mod test { 57 | use super::*; 58 | 59 | #[test] 60 | fn test_parse_error() { 61 | assert!(matches!( 62 | PositiveNonzeroInteger::parse("not a number"), 63 | Err(ParsePosNonzeroError::ParseInt(_)), 64 | )); 65 | } 66 | 67 | #[test] 68 | fn test_negative() { 69 | assert_eq!( 70 | PositiveNonzeroInteger::parse("-555"), 71 | Err(ParsePosNonzeroError::Creation(CreationError::Negative)), 72 | ); 73 | } 74 | 75 | #[test] 76 | fn test_zero() { 77 | assert_eq!( 78 | PositiveNonzeroInteger::parse("0"), 79 | Err(ParsePosNonzeroError::Creation(CreationError::Zero)), 80 | ); 81 | } 82 | 83 | #[test] 84 | fn test_positive() { 85 | let x = PositiveNonzeroInteger::new(42).unwrap(); 86 | assert_eq!(x.0, 42); 87 | assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x)); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /exercises/14_generics/README.md: -------------------------------------------------------------------------------- 1 | # Generics 2 | 3 | Generics is the topic of generalizing types and functionalities to broader cases. 4 | This is extremely useful for reducing code duplication in many ways, but can call for some rather involved syntax. 5 | Namely, being generic requires taking great care to specify over which types a generic type is actually considered valid. 6 | The simplest and most common use of generics is for type parameters. 7 | 8 | ## Further information 9 | 10 | - [Generic Data Types](https://doc.rust-lang.org/book/ch10-01-syntax.html) 11 | - [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html) 12 | -------------------------------------------------------------------------------- /exercises/14_generics/generics1.rs: -------------------------------------------------------------------------------- 1 | // `Vec` is generic over the type `T`. In most cases, the compiler is able to 2 | // infer `T`, for example after pushing a value with a concrete type to the vector. 3 | // But in this exercise, the compiler needs some help through a type annotation. 4 | 5 | fn main() { 6 | // TODO: Fix the compiler error by annotating the type of the vector 7 | // `Vec`. Choose `T` as some integer type that can be created from 8 | // `u8` and `i8`. 9 | let mut numbers = Vec::new(); 10 | 11 | // Don't change the lines below. 12 | let n1: u8 = 42; 13 | numbers.push(n1.into()); 14 | let n2: i8 = -1; 15 | numbers.push(n2.into()); 16 | 17 | println!("{numbers:?}"); 18 | } 19 | -------------------------------------------------------------------------------- /exercises/14_generics/generics2.rs: -------------------------------------------------------------------------------- 1 | // This powerful wrapper provides the ability to store a positive integer value. 2 | // TODO: Rewrite it using a generic so that it supports wrapping ANY type. 3 | struct Wrapper { 4 | value: u32, 5 | } 6 | 7 | // TODO: Adapt the struct's implementation to be generic over the wrapped value. 8 | impl Wrapper { 9 | fn new(value: u32) -> Self { 10 | Wrapper { value } 11 | } 12 | } 13 | 14 | fn main() { 15 | // You can optionally experiment here. 16 | } 17 | 18 | #[cfg(test)] 19 | mod tests { 20 | use super::*; 21 | 22 | #[test] 23 | fn store_u32_in_wrapper() { 24 | assert_eq!(Wrapper::new(42).value, 42); 25 | } 26 | 27 | #[test] 28 | fn store_str_in_wrapper() { 29 | assert_eq!(Wrapper::new("Foo").value, "Foo"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /exercises/15_traits/README.md: -------------------------------------------------------------------------------- 1 | # Traits 2 | 3 | A trait is a collection of methods. 4 | 5 | Data types can implement traits. To do so, the methods making up the trait are defined for the data type. For example, the `String` data type implements the `From<&str>` trait. This allows a user to write `String::from("hello")`. 6 | 7 | In this way, traits are somewhat similar to Java interfaces and C++ abstract classes. 8 | 9 | Some additional common Rust traits include: 10 | 11 | - `Clone` (the `clone` method) 12 | - `Display` (which allows formatted display via `{}`) 13 | - `Debug` (which allows formatted display via `{:?}`) 14 | 15 | Because traits indicate shared behavior between data types, they are useful when writing generics. 16 | 17 | ## Further information 18 | 19 | - [Traits](https://doc.rust-lang.org/book/ch10-02-traits.html) 20 | -------------------------------------------------------------------------------- /exercises/15_traits/traits1.rs: -------------------------------------------------------------------------------- 1 | // The trait `AppendBar` has only one function which appends "Bar" to any object 2 | // implementing this trait. 3 | trait AppendBar { 4 | fn append_bar(self) -> Self; 5 | } 6 | 7 | impl AppendBar for String { 8 | // TODO: Implement `AppendBar` for the type `String`. 9 | } 10 | 11 | fn main() { 12 | let s = String::from("Foo"); 13 | let s = s.append_bar(); 14 | println!("s: {s}"); 15 | } 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use super::*; 20 | 21 | #[test] 22 | fn is_foo_bar() { 23 | assert_eq!(String::from("Foo").append_bar(), "FooBar"); 24 | } 25 | 26 | #[test] 27 | fn is_bar_bar() { 28 | assert_eq!(String::from("").append_bar().append_bar(), "BarBar"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /exercises/15_traits/traits2.rs: -------------------------------------------------------------------------------- 1 | trait AppendBar { 2 | fn append_bar(self) -> Self; 3 | } 4 | 5 | // TODO: Implement the trait `AppendBar` for a vector of strings. 6 | // `append_bar` should push the string "Bar" into the vector. 7 | 8 | fn main() { 9 | // You can optionally experiment here. 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use super::*; 15 | 16 | #[test] 17 | fn is_vec_pop_eq_bar() { 18 | let mut foo = vec![String::from("Foo")].append_bar(); 19 | assert_eq!(foo.pop().unwrap(), "Bar"); 20 | assert_eq!(foo.pop().unwrap(), "Foo"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /exercises/15_traits/traits3.rs: -------------------------------------------------------------------------------- 1 | trait Licensed { 2 | // TODO: Add a default implementation for `licensing_info` so that 3 | // implementors like the two structs below can share that default behavior 4 | // without repeating the function. 5 | // The default license information should be the string "Default license". 6 | fn licensing_info(&self) -> String; 7 | } 8 | 9 | struct SomeSoftware { 10 | version_number: i32, 11 | } 12 | 13 | struct OtherSoftware { 14 | version_number: String, 15 | } 16 | 17 | impl Licensed for SomeSoftware {} // Don't edit this line. 18 | impl Licensed for OtherSoftware {} // Don't edit this line. 19 | 20 | fn main() { 21 | // You can optionally experiment here. 22 | } 23 | 24 | #[cfg(test)] 25 | mod tests { 26 | use super::*; 27 | 28 | #[test] 29 | fn is_licensing_info_the_same() { 30 | let licensing_info = "Default license"; 31 | let some_software = SomeSoftware { version_number: 1 }; 32 | let other_software = OtherSoftware { 33 | version_number: "v2.0.0".to_string(), 34 | }; 35 | assert_eq!(some_software.licensing_info(), licensing_info); 36 | assert_eq!(other_software.licensing_info(), licensing_info); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /exercises/15_traits/traits4.rs: -------------------------------------------------------------------------------- 1 | trait Licensed { 2 | fn licensing_info(&self) -> String { 3 | "Default license".to_string() 4 | } 5 | } 6 | 7 | struct SomeSoftware; 8 | struct OtherSoftware; 9 | 10 | impl Licensed for SomeSoftware {} 11 | impl Licensed for OtherSoftware {} 12 | 13 | // TODO: Fix the compiler error by only changing the signature of this function. 14 | fn compare_license_types(software1: ???, software2: ???) -> bool { 15 | software1.licensing_info() == software2.licensing_info() 16 | } 17 | 18 | fn main() { 19 | // You can optionally experiment here. 20 | } 21 | 22 | #[cfg(test)] 23 | mod tests { 24 | use super::*; 25 | 26 | #[test] 27 | fn compare_license_information() { 28 | assert!(compare_license_types(SomeSoftware, OtherSoftware)); 29 | } 30 | 31 | #[test] 32 | fn compare_license_information_backwards() { 33 | assert!(compare_license_types(OtherSoftware, SomeSoftware)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /exercises/15_traits/traits5.rs: -------------------------------------------------------------------------------- 1 | trait SomeTrait { 2 | fn some_function(&self) -> bool { 3 | true 4 | } 5 | } 6 | 7 | trait OtherTrait { 8 | fn other_function(&self) -> bool { 9 | true 10 | } 11 | } 12 | 13 | struct SomeStruct; 14 | impl SomeTrait for SomeStruct {} 15 | impl OtherTrait for SomeStruct {} 16 | 17 | struct OtherStruct; 18 | impl SomeTrait for OtherStruct {} 19 | impl OtherTrait for OtherStruct {} 20 | 21 | // TODO: Fix the compiler error by only changing the signature of this function. 22 | fn some_func(item: ???) -> bool { 23 | item.some_function() && item.other_function() 24 | } 25 | 26 | fn main() { 27 | // You can optionally experiment here. 28 | } 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | use super::*; 33 | 34 | #[test] 35 | fn test_some_func() { 36 | assert!(some_func(SomeStruct)); 37 | assert!(some_func(OtherStruct)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /exercises/16_lifetimes/README.md: -------------------------------------------------------------------------------- 1 | # Lifetimes 2 | 3 | Lifetimes tell the compiler how to check whether references live long 4 | enough to be valid in any given situation. For example lifetimes say 5 | "make sure parameter 'a' lives as long as parameter 'b' so that the return 6 | value is valid". 7 | 8 | They are only necessary on borrows, i.e. references, 9 | since copied parameters or moves are owned in their scope and cannot 10 | be referenced outside. Lifetimes mean that calling code of e.g. functions 11 | can be checked to make sure their arguments are valid. Lifetimes are 12 | restrictive of their callers. 13 | 14 | If you'd like to learn more about lifetime annotations, the 15 | [lifetimekata](https://tfpk.github.io/lifetimekata/) project 16 | has a similar style of exercises to Rustlings, but is all about 17 | learning to write lifetime annotations. 18 | 19 | ## Further information 20 | 21 | - [Lifetimes (in Rust By Example)](https://doc.rust-lang.org/stable/rust-by-example/scope/lifetime.html) 22 | - [Validating References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) 23 | -------------------------------------------------------------------------------- /exercises/16_lifetimes/lifetimes1.rs: -------------------------------------------------------------------------------- 1 | // The Rust compiler needs to know how to check whether supplied references are 2 | // valid, so that it can let the programmer know if a reference is at risk of 3 | // going out of scope before it is used. Remember, references are borrows and do 4 | // not own their own data. What if their owner goes out of scope? 5 | 6 | // TODO: Fix the compiler error by updating the function signature. 7 | fn longest(x: &str, y: &str) -> &str { 8 | if x.len() > y.len() { 9 | x 10 | } else { 11 | y 12 | } 13 | } 14 | 15 | fn main() { 16 | // You can optionally experiment here. 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | 23 | #[test] 24 | fn test_longest() { 25 | assert_eq!(longest("abcd", "123"), "abcd"); 26 | assert_eq!(longest("abc", "1234"), "1234"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /exercises/16_lifetimes/lifetimes2.rs: -------------------------------------------------------------------------------- 1 | // Don't change this function. 2 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { 3 | if x.len() > y.len() { 4 | x 5 | } else { 6 | y 7 | } 8 | } 9 | 10 | fn main() { 11 | // TODO: Fix the compiler error by moving one line. 12 | 13 | let string1 = String::from("long string is long"); 14 | let result; 15 | { 16 | let string2 = String::from("xyz"); 17 | result = longest(&string1, &string2); 18 | } 19 | println!("The longest string is '{result}'"); 20 | } 21 | -------------------------------------------------------------------------------- /exercises/16_lifetimes/lifetimes3.rs: -------------------------------------------------------------------------------- 1 | // Lifetimes are also needed when structs hold references. 2 | 3 | // TODO: Fix the compiler errors about the struct. 4 | struct Book { 5 | author: &str, 6 | title: &str, 7 | } 8 | 9 | fn main() { 10 | let book = Book { 11 | author: "George Orwell", 12 | title: "1984", 13 | }; 14 | 15 | println!("{} by {}", book.title, book.author); 16 | } 17 | -------------------------------------------------------------------------------- /exercises/17_tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | Going out of order from the book to cover tests -- many of the following exercises will ask you to make tests pass! 4 | 5 | ## Further information 6 | 7 | - [Writing Tests](https://doc.rust-lang.org/book/ch11-01-writing-tests.html) 8 | -------------------------------------------------------------------------------- /exercises/17_tests/tests1.rs: -------------------------------------------------------------------------------- 1 | // Tests are important to ensure that your code does what you think it should 2 | // do. 3 | 4 | fn is_even(n: i64) -> bool { 5 | n % 2 == 0 6 | } 7 | 8 | fn main() { 9 | // You can optionally experiment here. 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | // TODO: Import `is_even`. You can use a wildcard to import everything in 15 | // the outer module. 16 | 17 | #[test] 18 | fn you_can_assert() { 19 | // TODO: Test the function `is_even` with some values. 20 | assert!(); 21 | assert!(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /exercises/17_tests/tests2.rs: -------------------------------------------------------------------------------- 1 | // Calculates the power of 2 using a bit shift. 2 | // `1 << n` is equivalent to "2 to the power of n". 3 | fn power_of_2(n: u8) -> u64 { 4 | 1 << n 5 | } 6 | 7 | fn main() { 8 | // You can optionally experiment here. 9 | } 10 | 11 | #[cfg(test)] 12 | mod tests { 13 | use super::*; 14 | 15 | #[test] 16 | fn you_can_assert_eq() { 17 | // TODO: Test the function `power_of_2` with some values. 18 | assert_eq!(); 19 | assert_eq!(); 20 | assert_eq!(); 21 | assert_eq!(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /exercises/17_tests/tests3.rs: -------------------------------------------------------------------------------- 1 | struct Rectangle { 2 | width: i32, 3 | height: i32, 4 | } 5 | 6 | impl Rectangle { 7 | // Don't change this function. 8 | fn new(width: i32, height: i32) -> Self { 9 | if width <= 0 || height <= 0 { 10 | // Returning a `Result` would be better here. But we want to learn 11 | // how to test functions that can panic. 12 | panic!("Rectangle width and height must be positive"); 13 | } 14 | 15 | Rectangle { width, height } 16 | } 17 | } 18 | 19 | fn main() { 20 | // You can optionally experiment here. 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn correct_width_and_height() { 29 | // TODO: This test should check if the rectangle has the size that we 30 | // pass to its constructor. 31 | let rect = Rectangle::new(10, 20); 32 | assert_eq!(todo!(), 10); // Check width 33 | assert_eq!(todo!(), 20); // Check height 34 | } 35 | 36 | // TODO: This test should check if the program panics when we try to create 37 | // a rectangle with negative width. 38 | #[test] 39 | fn negative_width() { 40 | let _rect = Rectangle::new(-10, 10); 41 | } 42 | 43 | // TODO: This test should check if the program panics when we try to create 44 | // a rectangle with negative height. 45 | #[test] 46 | fn negative_height() { 47 | let _rect = Rectangle::new(10, -10); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /exercises/18_iterators/README.md: -------------------------------------------------------------------------------- 1 | # Iterators 2 | 3 | This section will teach you about Iterators. 4 | 5 | ## Further information 6 | 7 | - [Iterator](https://doc.rust-lang.org/book/ch13-02-iterators.html) 8 | - [Iterator documentation](https://doc.rust-lang.org/stable/std/iter/) 9 | -------------------------------------------------------------------------------- /exercises/18_iterators/iterators1.rs: -------------------------------------------------------------------------------- 1 | // When performing operations on elements within a collection, iterators are 2 | // essential. This module helps you get familiar with the structure of using an 3 | // iterator and how to go through elements within an iterable collection. 4 | 5 | fn main() { 6 | // You can optionally experiment here. 7 | } 8 | 9 | #[cfg(test)] 10 | mod tests { 11 | #[test] 12 | fn iterators() { 13 | let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; 14 | 15 | // TODO: Create an iterator over the array. 16 | let mut fav_fruits_iterator = todo!(); 17 | 18 | assert_eq!(fav_fruits_iterator.next(), Some(&"banana")); 19 | assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` 20 | assert_eq!(fav_fruits_iterator.next(), Some(&"avocado")); 21 | assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` 22 | assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); 23 | assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /exercises/18_iterators/iterators2.rs: -------------------------------------------------------------------------------- 1 | // In this exercise, you'll learn some of the unique advantages that iterators 2 | // can offer. 3 | 4 | // TODO: Complete the `capitalize_first` function. 5 | // "hello" -> "Hello" 6 | fn capitalize_first(input: &str) -> String { 7 | let mut chars = input.chars(); 8 | match chars.next() { 9 | None => String::new(), 10 | Some(first) => todo!(), 11 | } 12 | } 13 | 14 | // TODO: Apply the `capitalize_first` function to a slice of string slices. 15 | // Return a vector of strings. 16 | // ["hello", "world"] -> ["Hello", "World"] 17 | fn capitalize_words_vector(words: &[&str]) -> Vec { 18 | // ??? 19 | } 20 | 21 | // TODO: Apply the `capitalize_first` function again to a slice of string 22 | // slices. Return a single string. 23 | // ["hello", " ", "world"] -> "Hello World" 24 | fn capitalize_words_string(words: &[&str]) -> String { 25 | // ??? 26 | } 27 | 28 | fn main() { 29 | // You can optionally experiment here. 30 | } 31 | 32 | #[cfg(test)] 33 | mod tests { 34 | use super::*; 35 | 36 | #[test] 37 | fn test_success() { 38 | assert_eq!(capitalize_first("hello"), "Hello"); 39 | } 40 | 41 | #[test] 42 | fn test_empty() { 43 | assert_eq!(capitalize_first(""), ""); 44 | } 45 | 46 | #[test] 47 | fn test_iterate_string_vec() { 48 | let words = vec!["hello", "world"]; 49 | assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]); 50 | } 51 | 52 | #[test] 53 | fn test_iterate_into_string() { 54 | let words = vec!["hello", " ", "world"]; 55 | assert_eq!(capitalize_words_string(&words), "Hello World"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /exercises/18_iterators/iterators3.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Eq)] 2 | enum DivisionError { 3 | // Example: 42 / 0 4 | DivideByZero, 5 | // Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1` 6 | IntegerOverflow, 7 | // Example: 5 / 2 = 2.5 8 | NotDivisible, 9 | } 10 | 11 | // TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`. 12 | // Otherwise, return a suitable error. 13 | fn divide(a: i64, b: i64) -> Result { 14 | todo!(); 15 | } 16 | 17 | // TODO: Add the correct return type and complete the function body. 18 | // Desired output: `Ok([1, 11, 1426, 3])` 19 | fn result_with_list() { 20 | let numbers = [27, 297, 38502, 81]; 21 | let division_results = numbers.into_iter().map(|n| divide(n, 27)); 22 | } 23 | 24 | // TODO: Add the correct return type and complete the function body. 25 | // Desired output: `[Ok(1), Ok(11), Ok(1426), Ok(3)]` 26 | fn list_of_results() { 27 | let numbers = [27, 297, 38502, 81]; 28 | let division_results = numbers.into_iter().map(|n| divide(n, 27)); 29 | } 30 | 31 | fn main() { 32 | // You can optionally experiment here. 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | 39 | #[test] 40 | fn test_success() { 41 | assert_eq!(divide(81, 9), Ok(9)); 42 | } 43 | 44 | #[test] 45 | fn test_divide_by_0() { 46 | assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); 47 | } 48 | 49 | #[test] 50 | fn test_integer_overflow() { 51 | assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow)); 52 | } 53 | 54 | #[test] 55 | fn test_not_divisible() { 56 | assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); 57 | } 58 | 59 | #[test] 60 | fn test_divide_0_by_something() { 61 | assert_eq!(divide(0, 81), Ok(0)); 62 | } 63 | 64 | #[test] 65 | fn test_result_with_list() { 66 | assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]); 67 | } 68 | 69 | #[test] 70 | fn test_list_of_results() { 71 | assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /exercises/18_iterators/iterators4.rs: -------------------------------------------------------------------------------- 1 | fn factorial(num: u64) -> u64 { 2 | // TODO: Complete this function to return the factorial of `num` which is 3 | // defined as `1 * 2 * 3 * … * num`. 4 | // https://en.wikipedia.org/wiki/Factorial 5 | // 6 | // Do not use: 7 | // - early returns (using the `return` keyword explicitly) 8 | // Try not to use: 9 | // - imperative style loops (for/while) 10 | // - additional variables 11 | // For an extra challenge, don't use: 12 | // - recursion 13 | } 14 | 15 | fn main() { 16 | // You can optionally experiment here. 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | 23 | #[test] 24 | fn factorial_of_0() { 25 | assert_eq!(factorial(0), 1); 26 | } 27 | 28 | #[test] 29 | fn factorial_of_1() { 30 | assert_eq!(factorial(1), 1); 31 | } 32 | #[test] 33 | fn factorial_of_2() { 34 | assert_eq!(factorial(2), 2); 35 | } 36 | 37 | #[test] 38 | fn factorial_of_4() { 39 | assert_eq!(factorial(4), 24); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /exercises/18_iterators/iterators5.rs: -------------------------------------------------------------------------------- 1 | // Let's define a simple model to track Rustlings' exercise progress. Progress 2 | // will be modelled using a hash map. The name of the exercise is the key and 3 | // the progress is the value. Two counting functions were created to count the 4 | // number of exercises with a given progress. Recreate this counting 5 | // functionality using iterators. Try to not use imperative loops (for/while). 6 | 7 | use std::collections::HashMap; 8 | 9 | #[derive(Clone, Copy, PartialEq, Eq)] 10 | enum Progress { 11 | None, 12 | Some, 13 | Complete, 14 | } 15 | 16 | fn count_for(map: &HashMap, value: Progress) -> usize { 17 | let mut count = 0; 18 | for val in map.values() { 19 | if *val == value { 20 | count += 1; 21 | } 22 | } 23 | count 24 | } 25 | 26 | // TODO: Implement the functionality of `count_for` but with an iterator instead 27 | // of a `for` loop. 28 | fn count_iterator(map: &HashMap, value: Progress) -> usize { 29 | // `map` is a hash map with `String` keys and `Progress` values. 30 | // map = { "variables1": Complete, "from_str": None, … } 31 | } 32 | 33 | fn count_collection_for(collection: &[HashMap], value: Progress) -> usize { 34 | let mut count = 0; 35 | for map in collection { 36 | for val in map.values() { 37 | if *val == value { 38 | count += 1; 39 | } 40 | } 41 | } 42 | count 43 | } 44 | 45 | // TODO: Implement the functionality of `count_collection_for` but with an 46 | // iterator instead of a `for` loop. 47 | fn count_collection_iterator(collection: &[HashMap], value: Progress) -> usize { 48 | // `collection` is a slice of hash maps. 49 | // collection = [{ "variables1": Complete, "from_str": None, … }, 50 | // { "variables2": Complete, … }, … ] 51 | } 52 | 53 | fn main() { 54 | // You can optionally experiment here. 55 | } 56 | 57 | #[cfg(test)] 58 | mod tests { 59 | use super::*; 60 | 61 | fn get_map() -> HashMap { 62 | use Progress::*; 63 | 64 | let mut map = HashMap::new(); 65 | map.insert(String::from("variables1"), Complete); 66 | map.insert(String::from("functions1"), Complete); 67 | map.insert(String::from("hashmap1"), Complete); 68 | map.insert(String::from("arc1"), Some); 69 | map.insert(String::from("as_ref_mut"), None); 70 | map.insert(String::from("from_str"), None); 71 | 72 | map 73 | } 74 | 75 | fn get_vec_map() -> Vec> { 76 | use Progress::*; 77 | 78 | let map = get_map(); 79 | 80 | let mut other = HashMap::new(); 81 | other.insert(String::from("variables2"), Complete); 82 | other.insert(String::from("functions2"), Complete); 83 | other.insert(String::from("if1"), Complete); 84 | other.insert(String::from("from_into"), None); 85 | other.insert(String::from("try_from_into"), None); 86 | 87 | vec![map, other] 88 | } 89 | 90 | #[test] 91 | fn count_complete() { 92 | let map = get_map(); 93 | assert_eq!(count_iterator(&map, Progress::Complete), 3); 94 | } 95 | 96 | #[test] 97 | fn count_some() { 98 | let map = get_map(); 99 | assert_eq!(count_iterator(&map, Progress::Some), 1); 100 | } 101 | 102 | #[test] 103 | fn count_none() { 104 | let map = get_map(); 105 | assert_eq!(count_iterator(&map, Progress::None), 2); 106 | } 107 | 108 | #[test] 109 | fn count_complete_equals_for() { 110 | let map = get_map(); 111 | let progress_states = [Progress::Complete, Progress::Some, Progress::None]; 112 | for progress_state in progress_states { 113 | assert_eq!( 114 | count_for(&map, progress_state), 115 | count_iterator(&map, progress_state), 116 | ); 117 | } 118 | } 119 | 120 | #[test] 121 | fn count_collection_complete() { 122 | let collection = get_vec_map(); 123 | assert_eq!( 124 | count_collection_iterator(&collection, Progress::Complete), 125 | 6, 126 | ); 127 | } 128 | 129 | #[test] 130 | fn count_collection_some() { 131 | let collection = get_vec_map(); 132 | assert_eq!(count_collection_iterator(&collection, Progress::Some), 1); 133 | } 134 | 135 | #[test] 136 | fn count_collection_none() { 137 | let collection = get_vec_map(); 138 | assert_eq!(count_collection_iterator(&collection, Progress::None), 4); 139 | } 140 | 141 | #[test] 142 | fn count_collection_equals_for() { 143 | let collection = get_vec_map(); 144 | let progress_states = [Progress::Complete, Progress::Some, Progress::None]; 145 | 146 | for progress_state in progress_states { 147 | assert_eq!( 148 | count_collection_for(&collection, progress_state), 149 | count_collection_iterator(&collection, progress_state), 150 | ); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /exercises/19_smart_pointers/README.md: -------------------------------------------------------------------------------- 1 | # Smart Pointers 2 | 3 | In Rust, smart pointers are variables that contain an address in memory and reference some other data, but they also have additional metadata and capabilities. 4 | Smart pointers in Rust often own the data they point to, while references only borrow data. 5 | 6 | ## Further Information 7 | 8 | - [Smart Pointers](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html) 9 | - [Using Box to Point to Data on the Heap](https://doc.rust-lang.org/book/ch15-01-box.html) 10 | - [Rc\, the Reference Counted Smart Pointer](https://doc.rust-lang.org/book/ch15-04-rc.html) 11 | - [Shared-State Concurrency](https://doc.rust-lang.org/book/ch16-03-shared-state.html) 12 | - [Cow Documentation](https://doc.rust-lang.org/std/borrow/enum.Cow.html) 13 | -------------------------------------------------------------------------------- /exercises/19_smart_pointers/arc1.rs: -------------------------------------------------------------------------------- 1 | // In this exercise, we are given a `Vec` of `u32` called `numbers` with values 2 | // ranging from 0 to 99. We would like to use this set of numbers within 8 3 | // different threads simultaneously. Each thread is going to get the sum of 4 | // every eighth value with an offset. 5 | // 6 | // The first thread (offset 0), will sum 0, 8, 16, … 7 | // The second thread (offset 1), will sum 1, 9, 17, … 8 | // The third thread (offset 2), will sum 2, 10, 18, … 9 | // … 10 | // The eighth thread (offset 7), will sum 7, 15, 23, … 11 | // 12 | // Each thread should own a reference-counting pointer to the vector of 13 | // numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`. 14 | // 15 | // Don't get distracted by how threads are spawned and joined. We will practice 16 | // that later in the exercises about threads. 17 | 18 | // Don't change the lines below. 19 | #![forbid(unused_imports)] 20 | use std::{sync::Arc, thread}; 21 | 22 | fn main() { 23 | let numbers: Vec<_> = (0..100u32).collect(); 24 | 25 | // TODO: Define `shared_numbers` by using `Arc`. 26 | // let shared_numbers = ???; 27 | 28 | let mut join_handles = Vec::new(); 29 | 30 | for offset in 0..8 { 31 | // TODO: Define `child_numbers` using `shared_numbers`. 32 | // let child_numbers = ???; 33 | 34 | let handle = thread::spawn(move || { 35 | let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); 36 | println!("Sum of offset {offset} is {sum}"); 37 | }); 38 | 39 | join_handles.push(handle); 40 | } 41 | 42 | for handle in join_handles.into_iter() { 43 | handle.join().unwrap(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /exercises/19_smart_pointers/box1.rs: -------------------------------------------------------------------------------- 1 | // At compile time, Rust needs to know how much space a type takes up. This 2 | // becomes problematic for recursive types, where a value can have as part of 3 | // itself another value of the same type. To get around the issue, we can use a 4 | // `Box` - a smart pointer used to store data on the heap, which also allows us 5 | // to wrap a recursive type. 6 | // 7 | // The recursive type we're implementing in this exercise is the "cons list", a 8 | // data structure frequently found in functional programming languages. Each 9 | // item in a cons list contains two elements: The value of the current item and 10 | // the next item. The last item is a value called `Nil`. 11 | 12 | // TODO: Use a `Box` in the enum definition to make the code compile. 13 | #[derive(PartialEq, Debug)] 14 | enum List { 15 | Cons(i32, List), 16 | Nil, 17 | } 18 | 19 | // TODO: Create an empty cons list. 20 | fn create_empty_list() -> List { 21 | todo!() 22 | } 23 | 24 | // TODO: Create a non-empty cons list. 25 | fn create_non_empty_list() -> List { 26 | todo!() 27 | } 28 | 29 | fn main() { 30 | println!("This is an empty cons list: {:?}", create_empty_list()); 31 | println!( 32 | "This is a non-empty cons list: {:?}", 33 | create_non_empty_list(), 34 | ); 35 | } 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | use super::*; 40 | 41 | #[test] 42 | fn test_create_empty_list() { 43 | assert_eq!(create_empty_list(), List::Nil); 44 | } 45 | 46 | #[test] 47 | fn test_create_non_empty_list() { 48 | assert_ne!(create_empty_list(), create_non_empty_list()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /exercises/19_smart_pointers/cow1.rs: -------------------------------------------------------------------------------- 1 | // This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can 2 | // enclose and provide immutable access to borrowed data and clone the data 3 | // lazily when mutation or ownership is required. The type is designed to work 4 | // with general borrowed data via the `Borrow` trait. 5 | 6 | use std::borrow::Cow; 7 | 8 | fn abs_all(input: &mut Cow<[i32]>) { 9 | for ind in 0..input.len() { 10 | let value = input[ind]; 11 | if value < 0 { 12 | // Clones into a vector if not already owned. 13 | input.to_mut()[ind] = -value; 14 | } 15 | } 16 | } 17 | 18 | fn main() { 19 | // You can optionally experiment here. 20 | } 21 | 22 | #[cfg(test)] 23 | mod tests { 24 | use super::*; 25 | 26 | #[test] 27 | fn reference_mutation() { 28 | // Clone occurs because `input` needs to be mutated. 29 | let vec = vec![-1, 0, 1]; 30 | let mut input = Cow::from(&vec); 31 | abs_all(&mut input); 32 | assert!(matches!(input, Cow::Owned(_))); 33 | } 34 | 35 | #[test] 36 | fn reference_no_mutation() { 37 | // No clone occurs because `input` doesn't need to be mutated. 38 | let vec = vec![0, 1, 2]; 39 | let mut input = Cow::from(&vec); 40 | abs_all(&mut input); 41 | // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. 42 | assert!(matches!(input, todo!())); 43 | } 44 | 45 | #[test] 46 | fn owned_no_mutation() { 47 | // We can also pass `vec` without `&` so `Cow` owns it directly. In this 48 | // case, no mutation occurs (all numbers are already absolute) and thus 49 | // also no clone. But the result is still owned because it was never 50 | // borrowed or mutated. 51 | let vec = vec![0, 1, 2]; 52 | let mut input = Cow::from(vec); 53 | abs_all(&mut input); 54 | // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. 55 | assert!(matches!(input, todo!())); 56 | } 57 | 58 | #[test] 59 | fn owned_mutation() { 60 | // Of course this is also the case if a mutation does occur (not all 61 | // numbers are absolute). In this case, the call to `to_mut()` in the 62 | // `abs_all` function returns a reference to the same data as before. 63 | let vec = vec![-1, 0, 1]; 64 | let mut input = Cow::from(vec); 65 | abs_all(&mut input); 66 | // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. 67 | assert!(matches!(input, todo!())); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /exercises/19_smart_pointers/rc1.rs: -------------------------------------------------------------------------------- 1 | // In this exercise, we want to express the concept of multiple owners via the 2 | // `Rc` type. This is a model of our solar system - there is a `Sun` type and 3 | // multiple `Planet`s. The planets take ownership of the sun, indicating that 4 | // they revolve around the sun. 5 | 6 | use std::rc::Rc; 7 | 8 | #[derive(Debug)] 9 | struct Sun; 10 | 11 | #[derive(Debug)] 12 | enum Planet { 13 | Mercury(Rc), 14 | Venus(Rc), 15 | Earth(Rc), 16 | Mars(Rc), 17 | Jupiter(Rc), 18 | Saturn(Rc), 19 | Uranus(Rc), 20 | Neptune(Rc), 21 | } 22 | 23 | impl Planet { 24 | fn details(&self) { 25 | println!("Hi from {self:?}!"); 26 | } 27 | } 28 | 29 | fn main() { 30 | // You can optionally experiment here. 31 | } 32 | 33 | #[cfg(test)] 34 | mod tests { 35 | use super::*; 36 | 37 | #[test] 38 | fn rc1() { 39 | let sun = Rc::new(Sun); 40 | println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference 41 | 42 | let mercury = Planet::Mercury(Rc::clone(&sun)); 43 | println!("reference count = {}", Rc::strong_count(&sun)); // 2 references 44 | mercury.details(); 45 | 46 | let venus = Planet::Venus(Rc::clone(&sun)); 47 | println!("reference count = {}", Rc::strong_count(&sun)); // 3 references 48 | venus.details(); 49 | 50 | let earth = Planet::Earth(Rc::clone(&sun)); 51 | println!("reference count = {}", Rc::strong_count(&sun)); // 4 references 52 | earth.details(); 53 | 54 | let mars = Planet::Mars(Rc::clone(&sun)); 55 | println!("reference count = {}", Rc::strong_count(&sun)); // 5 references 56 | mars.details(); 57 | 58 | let jupiter = Planet::Jupiter(Rc::clone(&sun)); 59 | println!("reference count = {}", Rc::strong_count(&sun)); // 6 references 60 | jupiter.details(); 61 | 62 | // TODO 63 | let saturn = Planet::Saturn(Rc::new(Sun)); 64 | println!("reference count = {}", Rc::strong_count(&sun)); // 7 references 65 | saturn.details(); 66 | 67 | // TODO 68 | let uranus = Planet::Uranus(Rc::new(Sun)); 69 | println!("reference count = {}", Rc::strong_count(&sun)); // 8 references 70 | uranus.details(); 71 | 72 | // TODO 73 | let neptune = Planet::Neptune(Rc::new(Sun)); 74 | println!("reference count = {}", Rc::strong_count(&sun)); // 9 references 75 | neptune.details(); 76 | 77 | assert_eq!(Rc::strong_count(&sun), 9); 78 | 79 | drop(neptune); 80 | println!("reference count = {}", Rc::strong_count(&sun)); // 8 references 81 | 82 | drop(uranus); 83 | println!("reference count = {}", Rc::strong_count(&sun)); // 7 references 84 | 85 | drop(saturn); 86 | println!("reference count = {}", Rc::strong_count(&sun)); // 6 references 87 | 88 | drop(jupiter); 89 | println!("reference count = {}", Rc::strong_count(&sun)); // 5 references 90 | 91 | drop(mars); 92 | println!("reference count = {}", Rc::strong_count(&sun)); // 4 references 93 | 94 | // TODO 95 | println!("reference count = {}", Rc::strong_count(&sun)); // 3 references 96 | 97 | // TODO 98 | println!("reference count = {}", Rc::strong_count(&sun)); // 2 references 99 | 100 | // TODO 101 | println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference 102 | 103 | assert_eq!(Rc::strong_count(&sun), 1); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /exercises/20_threads/README.md: -------------------------------------------------------------------------------- 1 | # Threads 2 | 3 | In most current operating systems, an executed program's code is run in a process, and the operating system manages multiple processes at once. 4 | Within your program, you can also have independent parts that run simultaneously. The features that run these independent parts are called threads. 5 | 6 | ## Further information 7 | 8 | - [Dining Philosophers example](https://doc.rust-lang.org/1.4.0/book/dining-philosophers.html) 9 | - [Using Threads to Run Code Simultaneously](https://doc.rust-lang.org/book/ch16-01-threads.html) 10 | - [Using Message Passing to Transfer Data Between Threads](https://doc.rust-lang.org/book/ch16-02-message-passing.html) 11 | -------------------------------------------------------------------------------- /exercises/20_threads/threads1.rs: -------------------------------------------------------------------------------- 1 | // This program spawns multiple threads that each runs for at least 250ms, and 2 | // each thread returns how much time it took to complete. The program should 3 | // wait until all the spawned threads have finished and should collect their 4 | // return values into a vector. 5 | 6 | use std::{ 7 | thread, 8 | time::{Duration, Instant}, 9 | }; 10 | 11 | fn main() { 12 | let mut handles = Vec::new(); 13 | for i in 0..10 { 14 | let handle = thread::spawn(move || { 15 | let start = Instant::now(); 16 | thread::sleep(Duration::from_millis(250)); 17 | println!("Thread {i} done"); 18 | start.elapsed().as_millis() 19 | }); 20 | handles.push(handle); 21 | } 22 | 23 | let mut results = Vec::new(); 24 | for handle in handles { 25 | // TODO: Collect the results of all threads into the `results` vector. 26 | // Use the `JoinHandle` struct which is returned by `thread::spawn`. 27 | } 28 | 29 | if results.len() != 10 { 30 | panic!("Oh no! Some thread isn't done yet!"); 31 | } 32 | 33 | println!(); 34 | for (i, result) in results.into_iter().enumerate() { 35 | println!("Thread {i} took {result}ms"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /exercises/20_threads/threads2.rs: -------------------------------------------------------------------------------- 1 | // Building on the last exercise, we want all of the threads to complete their 2 | // work. But this time, the spawned threads need to be in charge of updating a 3 | // shared value: `JobStatus.jobs_done` 4 | 5 | use std::{sync::Arc, thread, time::Duration}; 6 | 7 | struct JobStatus { 8 | jobs_done: u32, 9 | } 10 | 11 | fn main() { 12 | // TODO: `Arc` isn't enough if you want a **mutable** shared state. 13 | let status = Arc::new(JobStatus { jobs_done: 0 }); 14 | 15 | let mut handles = Vec::new(); 16 | for _ in 0..10 { 17 | let status_shared = Arc::clone(&status); 18 | let handle = thread::spawn(move || { 19 | thread::sleep(Duration::from_millis(250)); 20 | 21 | // TODO: You must take an action before you update a shared value. 22 | status_shared.jobs_done += 1; 23 | }); 24 | handles.push(handle); 25 | } 26 | 27 | // Waiting for all jobs to complete. 28 | for handle in handles { 29 | handle.join().unwrap(); 30 | } 31 | 32 | // TODO: Print the value of `JobStatus.jobs_done`. 33 | println!("Jobs done: {}", todo!()); 34 | } 35 | -------------------------------------------------------------------------------- /exercises/20_threads/threads3.rs: -------------------------------------------------------------------------------- 1 | use std::{sync::mpsc, thread, time::Duration}; 2 | 3 | struct Queue { 4 | first_half: Vec, 5 | second_half: Vec, 6 | } 7 | 8 | impl Queue { 9 | fn new() -> Self { 10 | Self { 11 | first_half: vec![1, 2, 3, 4, 5], 12 | second_half: vec![6, 7, 8, 9, 10], 13 | } 14 | } 15 | } 16 | 17 | fn send_tx(q: Queue, tx: mpsc::Sender) { 18 | // TODO: We want to send `tx` to both threads. But currently, it is moved 19 | // into the first thread. How could you solve this problem? 20 | thread::spawn(move || { 21 | for val in q.first_half { 22 | println!("Sending {val:?}"); 23 | tx.send(val).unwrap(); 24 | thread::sleep(Duration::from_millis(250)); 25 | } 26 | }); 27 | 28 | thread::spawn(move || { 29 | for val in q.second_half { 30 | println!("Sending {val:?}"); 31 | tx.send(val).unwrap(); 32 | thread::sleep(Duration::from_millis(250)); 33 | } 34 | }); 35 | } 36 | 37 | fn main() { 38 | // You can optionally experiment here. 39 | } 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | 45 | #[test] 46 | fn threads3() { 47 | let (tx, rx) = mpsc::channel(); 48 | let queue = Queue::new(); 49 | 50 | send_tx(queue, tx); 51 | 52 | let mut received = Vec::with_capacity(10); 53 | for value in rx { 54 | received.push(value); 55 | } 56 | 57 | received.sort(); 58 | assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /exercises/21_macros/README.md: -------------------------------------------------------------------------------- 1 | # Macros 2 | 3 | Rust's macro system is very powerful, but also kind of difficult to wrap your 4 | head around. We're not going to teach you how to write your own fully-featured 5 | macros. Instead, we'll show you how to use and create them. 6 | 7 | If you'd like to learn more about writing your own macros, the 8 | [macrokata](https://github.com/tfpk/macrokata) project has a similar style 9 | of exercises to Rustlings, but is all about learning to write Macros. 10 | 11 | ## Further information 12 | 13 | - [Macros](https://doc.rust-lang.org/book/ch19-06-macros.html) 14 | - [The Little Book of Rust Macros](https://veykril.github.io/tlborm/) 15 | -------------------------------------------------------------------------------- /exercises/21_macros/macros1.rs: -------------------------------------------------------------------------------- 1 | macro_rules! my_macro { 2 | () => { 3 | println!("Check out my macro!"); 4 | }; 5 | } 6 | 7 | fn main() { 8 | // TODO: Fix the macro call. 9 | my_macro(); 10 | } 11 | -------------------------------------------------------------------------------- /exercises/21_macros/macros2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | my_macro!(); 3 | } 4 | 5 | // TODO: Fix the compiler error by moving the whole definition of this macro. 6 | macro_rules! my_macro { 7 | () => { 8 | println!("Check out my macro!"); 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /exercises/21_macros/macros3.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the compiler error without taking the macro definition out of this 2 | // module. 3 | mod macros { 4 | macro_rules! my_macro { 5 | () => { 6 | println!("Check out my macro!"); 7 | }; 8 | } 9 | } 10 | 11 | fn main() { 12 | my_macro!(); 13 | } 14 | -------------------------------------------------------------------------------- /exercises/21_macros/macros4.rs: -------------------------------------------------------------------------------- 1 | // TODO: Fix the compiler error by adding one or two characters. 2 | #[rustfmt::skip] 3 | macro_rules! my_macro { 4 | () => { 5 | println!("Check out my macro!"); 6 | } 7 | ($val:expr) => { 8 | println!("Look at this other macro: {}", $val); 9 | } 10 | } 11 | 12 | fn main() { 13 | my_macro!(); 14 | my_macro!(7777); 15 | } 16 | -------------------------------------------------------------------------------- /exercises/22_clippy/README.md: -------------------------------------------------------------------------------- 1 | # Clippy 2 | 3 | The Clippy tool is a collection of lints to analyze your code so you can catch common mistakes and improve your Rust code. 4 | 5 | If you used the installation script for Rustlings, Clippy should be already installed. 6 | If not you can install it manually via `rustup component add clippy`. 7 | 8 | ## Further information 9 | 10 | - [GitHub Repository](https://github.com/rust-lang/rust-clippy). 11 | -------------------------------------------------------------------------------- /exercises/22_clippy/clippy1.rs: -------------------------------------------------------------------------------- 1 | // The Clippy tool is a collection of lints to analyze your code so you can 2 | // catch common mistakes and improve your Rust code. 3 | // 4 | // For these exercises, the code will fail to compile when there are Clippy 5 | // warnings. Check Clippy's suggestions from the output to solve the exercise. 6 | 7 | fn main() { 8 | // TODO: Fix the Clippy lint in this line. 9 | let pi = 3.14; 10 | let radius: f32 = 5.0; 11 | 12 | let area = pi * radius.powi(2); 13 | 14 | println!("The area of a circle with radius {radius:.2} is {area:.5}"); 15 | } 16 | -------------------------------------------------------------------------------- /exercises/22_clippy/clippy2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut res = 42; 3 | let option = Some(12); 4 | // TODO: Fix the Clippy lint. 5 | for x in option { 6 | res += x; 7 | } 8 | 9 | println!("{res}"); 10 | } 11 | -------------------------------------------------------------------------------- /exercises/22_clippy/clippy3.rs: -------------------------------------------------------------------------------- 1 | // Here are some more easy Clippy fixes so you can see its utility 📎 2 | // TODO: Fix all the Clippy lints. 3 | 4 | #[rustfmt::skip] 5 | #[allow(unused_variables, unused_assignments)] 6 | fn main() { 7 | let my_option: Option<()> = None; 8 | if my_option.is_none() { 9 | println!("{:?}", my_option.unwrap()); 10 | } 11 | 12 | let my_arr = &[ 13 | -1, -2, -3 14 | -4, -5, -6 15 | ]; 16 | println!("My array! Here it is: {my_arr:?}"); 17 | 18 | let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5); 19 | println!("This Vec is empty, see? {my_empty_vec:?}"); 20 | 21 | let mut value_a = 45; 22 | let mut value_b = 66; 23 | // Let's swap these two! 24 | value_a = value_b; 25 | value_b = value_a; 26 | println!("value a: {value_a}; value b: {value_b}"); 27 | } 28 | -------------------------------------------------------------------------------- /exercises/23_conversions/README.md: -------------------------------------------------------------------------------- 1 | # Type conversions 2 | 3 | Rust offers a multitude of ways to convert a value of a given type into another type. 4 | 5 | The simplest form of type conversion is a type cast expression. It is denoted with the binary operator `as`. For instance, `println!("{}", 1 + 1.0);` would not compile, since `1` is an integer while `1.0` is a float. However, `println!("{}", 1 as f32 + 1.0)` should compile. The exercise [`using_as`](using_as.rs) tries to cover this. 6 | 7 | Rust also offers traits that facilitate type conversions upon implementation. These traits can be found under the [`convert`](https://doc.rust-lang.org/std/convert/index.html) module. 8 | The traits are the following: 9 | 10 | - `From` and `Into` covered in [`from_into`](from_into.rs) 11 | - `TryFrom` and `TryInto` covered in [`try_from_into`](try_from_into.rs) 12 | - `AsRef` and `AsMut` covered in [`as_ref_mut`](as_ref_mut.rs) 13 | 14 | Furthermore, the `std::str` module offers a trait called [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) which helps with converting strings into target types via the `parse` method on strings. If properly implemented for a given type `Person`, then `let p: Person = "Mark,20".parse().unwrap()` should both compile and run without panicking. 15 | 16 | These should be the main ways ***within the standard library*** to convert data into your desired types. 17 | 18 | ## Further information 19 | 20 | These are not directly covered in the book, but the standard library has a great documentation for it. 21 | 22 | - [conversions](https://doc.rust-lang.org/std/convert/index.html) 23 | - [`FromStr` trait](https://doc.rust-lang.org/std/str/trait.FromStr.html) 24 | -------------------------------------------------------------------------------- /exercises/23_conversions/as_ref_mut.rs: -------------------------------------------------------------------------------- 1 | // AsRef and AsMut allow for cheap reference-to-reference conversions. Read more 2 | // about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and 3 | // https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively. 4 | 5 | // Obtain the number of bytes (not characters) in the given argument 6 | // (`.len()` returns the number of bytes in a string). 7 | // TODO: Add the `AsRef` trait appropriately as a trait bound. 8 | fn byte_counter(arg: T) -> usize { 9 | arg.as_ref().len() 10 | } 11 | 12 | // Obtain the number of characters (not bytes) in the given argument. 13 | // TODO: Add the `AsRef` trait appropriately as a trait bound. 14 | fn char_counter(arg: T) -> usize { 15 | arg.as_ref().chars().count() 16 | } 17 | 18 | // Squares a number using `as_mut()`. 19 | // TODO: Add the appropriate trait bound. 20 | fn num_sq(arg: &mut T) { 21 | // TODO: Implement the function body. 22 | } 23 | 24 | fn main() { 25 | // You can optionally experiment here. 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn different_counts() { 34 | let s = "Café au lait"; 35 | assert_ne!(char_counter(s), byte_counter(s)); 36 | } 37 | 38 | #[test] 39 | fn same_counts() { 40 | let s = "Cafe au lait"; 41 | assert_eq!(char_counter(s), byte_counter(s)); 42 | } 43 | 44 | #[test] 45 | fn different_counts_using_string() { 46 | let s = String::from("Café au lait"); 47 | assert_ne!(char_counter(s.clone()), byte_counter(s)); 48 | } 49 | 50 | #[test] 51 | fn same_counts_using_string() { 52 | let s = String::from("Cafe au lait"); 53 | assert_eq!(char_counter(s.clone()), byte_counter(s)); 54 | } 55 | 56 | #[test] 57 | fn mut_box() { 58 | let mut num: Box = Box::new(3); 59 | num_sq(&mut num); 60 | assert_eq!(*num, 9); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /exercises/23_conversions/from_into.rs: -------------------------------------------------------------------------------- 1 | // The `From` trait is used for value-to-value conversions. If `From` is 2 | // implemented, an implementation of `Into` is automatically provided. 3 | // You can read more about it in the documentation: 4 | // https://doc.rust-lang.org/std/convert/trait.From.html 5 | 6 | #[derive(Debug)] 7 | struct Person { 8 | name: String, 9 | age: u8, 10 | } 11 | 12 | // We implement the Default trait to use it as a fallback when the provided 13 | // string is not convertible into a `Person` object. 14 | impl Default for Person { 15 | fn default() -> Self { 16 | Self { 17 | name: String::from("John"), 18 | age: 30, 19 | } 20 | } 21 | } 22 | 23 | // TODO: Complete this `From` implementation to be able to parse a `Person` 24 | // out of a string in the form of "Mark,20". 25 | // Note that you'll need to parse the age component into a `u8` with something 26 | // like `"4".parse::()`. 27 | // 28 | // Steps: 29 | // 1. Split the given string on the commas present in it. 30 | // 2. If the split operation returns less or more than 2 elements, return the 31 | // default of `Person`. 32 | // 3. Use the first element from the split operation as the name. 33 | // 4. If the name is empty, return the default of `Person`. 34 | // 5. Parse the second element from the split operation into a `u8` as the age. 35 | // 6. If parsing the age fails, return the default of `Person`. 36 | impl From<&str> for Person { 37 | fn from(s: &str) -> Self {} 38 | } 39 | 40 | fn main() { 41 | // Use the `from` function. 42 | let p1 = Person::from("Mark,20"); 43 | println!("{p1:?}"); 44 | 45 | // Since `From` is implemented for Person, we are able to use `Into`. 46 | let p2: Person = "Gerald,70".into(); 47 | println!("{p2:?}"); 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | use super::*; 53 | 54 | #[test] 55 | fn test_default() { 56 | let dp = Person::default(); 57 | assert_eq!(dp.name, "John"); 58 | assert_eq!(dp.age, 30); 59 | } 60 | 61 | #[test] 62 | fn test_bad_convert() { 63 | let p = Person::from(""); 64 | assert_eq!(p.name, "John"); 65 | assert_eq!(p.age, 30); 66 | } 67 | 68 | #[test] 69 | fn test_good_convert() { 70 | let p = Person::from("Mark,20"); 71 | assert_eq!(p.name, "Mark"); 72 | assert_eq!(p.age, 20); 73 | } 74 | 75 | #[test] 76 | fn test_bad_age() { 77 | let p = Person::from("Mark,twenty"); 78 | assert_eq!(p.name, "John"); 79 | assert_eq!(p.age, 30); 80 | } 81 | 82 | #[test] 83 | fn test_missing_comma_and_age() { 84 | let p: Person = Person::from("Mark"); 85 | assert_eq!(p.name, "John"); 86 | assert_eq!(p.age, 30); 87 | } 88 | 89 | #[test] 90 | fn test_missing_age() { 91 | let p: Person = Person::from("Mark,"); 92 | assert_eq!(p.name, "John"); 93 | assert_eq!(p.age, 30); 94 | } 95 | 96 | #[test] 97 | fn test_missing_name() { 98 | let p: Person = Person::from(",1"); 99 | assert_eq!(p.name, "John"); 100 | assert_eq!(p.age, 30); 101 | } 102 | 103 | #[test] 104 | fn test_missing_name_and_age() { 105 | let p: Person = Person::from(","); 106 | assert_eq!(p.name, "John"); 107 | assert_eq!(p.age, 30); 108 | } 109 | 110 | #[test] 111 | fn test_missing_name_and_invalid_age() { 112 | let p: Person = Person::from(",one"); 113 | assert_eq!(p.name, "John"); 114 | assert_eq!(p.age, 30); 115 | } 116 | 117 | #[test] 118 | fn test_trailing_comma() { 119 | let p: Person = Person::from("Mike,32,"); 120 | assert_eq!(p.name, "John"); 121 | assert_eq!(p.age, 30); 122 | } 123 | 124 | #[test] 125 | fn test_trailing_comma_and_some_string() { 126 | let p: Person = Person::from("Mike,32,dog"); 127 | assert_eq!(p.name, "John"); 128 | assert_eq!(p.age, 30); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /exercises/23_conversions/from_str.rs: -------------------------------------------------------------------------------- 1 | // This is similar to the previous `from_into` exercise. But this time, we'll 2 | // implement `FromStr` and return errors instead of falling back to a default 3 | // value. Additionally, upon implementing `FromStr`, you can use the `parse` 4 | // method on strings to generate an object of the implementor type. You can read 5 | // more about it in the documentation: 6 | // https://doc.rust-lang.org/std/str/trait.FromStr.html 7 | 8 | use std::num::ParseIntError; 9 | use std::str::FromStr; 10 | 11 | #[derive(Debug, PartialEq)] 12 | struct Person { 13 | name: String, 14 | age: u8, 15 | } 16 | 17 | // We will use this error type for the `FromStr` implementation. 18 | #[derive(Debug, PartialEq)] 19 | enum ParsePersonError { 20 | // Incorrect number of fields 21 | BadLen, 22 | // Empty name field 23 | NoName, 24 | // Wrapped error from parse::() 25 | ParseInt(ParseIntError), 26 | } 27 | 28 | // TODO: Complete this `FromStr` implementation to be able to parse a `Person` 29 | // out of a string in the form of "Mark,20". 30 | // Note that you'll need to parse the age component into a `u8` with something 31 | // like `"4".parse::()`. 32 | // 33 | // Steps: 34 | // 1. Split the given string on the commas present in it. 35 | // 2. If the split operation returns less or more than 2 elements, return the 36 | // error `ParsePersonError::BadLen`. 37 | // 3. Use the first element from the split operation as the name. 38 | // 4. If the name is empty, return the error `ParsePersonError::NoName`. 39 | // 5. Parse the second element from the split operation into a `u8` as the age. 40 | // 6. If parsing the age fails, return the error `ParsePersonError::ParseInt`. 41 | impl FromStr for Person { 42 | type Err = ParsePersonError; 43 | 44 | fn from_str(s: &str) -> Result {} 45 | } 46 | 47 | fn main() { 48 | let p = "Mark,20".parse::(); 49 | println!("{p:?}"); 50 | } 51 | 52 | #[cfg(test)] 53 | mod tests { 54 | use super::*; 55 | use ParsePersonError::*; 56 | 57 | #[test] 58 | fn empty_input() { 59 | assert_eq!("".parse::(), Err(BadLen)); 60 | } 61 | 62 | #[test] 63 | fn good_input() { 64 | let p = "John,32".parse::(); 65 | assert!(p.is_ok()); 66 | let p = p.unwrap(); 67 | assert_eq!(p.name, "John"); 68 | assert_eq!(p.age, 32); 69 | } 70 | 71 | #[test] 72 | fn missing_age() { 73 | assert!(matches!("John,".parse::(), Err(ParseInt(_)))); 74 | } 75 | 76 | #[test] 77 | fn invalid_age() { 78 | assert!(matches!("John,twenty".parse::(), Err(ParseInt(_)))); 79 | } 80 | 81 | #[test] 82 | fn missing_comma_and_age() { 83 | assert_eq!("John".parse::(), Err(BadLen)); 84 | } 85 | 86 | #[test] 87 | fn missing_name() { 88 | assert_eq!(",1".parse::(), Err(NoName)); 89 | } 90 | 91 | #[test] 92 | fn missing_name_and_age() { 93 | assert!(matches!(",".parse::(), Err(NoName | ParseInt(_)))); 94 | } 95 | 96 | #[test] 97 | fn missing_name_and_invalid_age() { 98 | assert!(matches!( 99 | ",one".parse::(), 100 | Err(NoName | ParseInt(_)), 101 | )); 102 | } 103 | 104 | #[test] 105 | fn trailing_comma() { 106 | assert_eq!("John,32,".parse::(), Err(BadLen)); 107 | } 108 | 109 | #[test] 110 | fn trailing_comma_and_some_string() { 111 | assert_eq!("John,32,man".parse::(), Err(BadLen)); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /exercises/23_conversions/try_from_into.rs: -------------------------------------------------------------------------------- 1 | // `TryFrom` is a simple and safe type conversion that may fail in a controlled 2 | // way under some circumstances. Basically, this is the same as `From`. The main 3 | // difference is that this should return a `Result` type instead of the target 4 | // type itself. You can read more about it in the documentation: 5 | // https://doc.rust-lang.org/std/convert/trait.TryFrom.html 6 | 7 | #![allow(clippy::useless_vec)] 8 | use std::convert::{TryFrom, TryInto}; 9 | 10 | #[derive(Debug, PartialEq)] 11 | struct Color { 12 | red: u8, 13 | green: u8, 14 | blue: u8, 15 | } 16 | 17 | // We will use this error type for the `TryFrom` conversions. 18 | #[derive(Debug, PartialEq)] 19 | enum IntoColorError { 20 | // Incorrect length of slice 21 | BadLen, 22 | // Integer conversion error 23 | IntConversion, 24 | } 25 | 26 | // TODO: Tuple implementation. 27 | // Correct RGB color values must be integers in the 0..=255 range. 28 | impl TryFrom<(i16, i16, i16)> for Color { 29 | type Error = IntoColorError; 30 | 31 | fn try_from(tuple: (i16, i16, i16)) -> Result {} 32 | } 33 | 34 | // TODO: Array implementation. 35 | impl TryFrom<[i16; 3]> for Color { 36 | type Error = IntoColorError; 37 | 38 | fn try_from(arr: [i16; 3]) -> Result {} 39 | } 40 | 41 | // TODO: Slice implementation. 42 | // This implementation needs to check the slice length. 43 | impl TryFrom<&[i16]> for Color { 44 | type Error = IntoColorError; 45 | 46 | fn try_from(slice: &[i16]) -> Result {} 47 | } 48 | 49 | fn main() { 50 | // Using the `try_from` function. 51 | let c1 = Color::try_from((183, 65, 14)); 52 | println!("{c1:?}"); 53 | 54 | // Since `TryFrom` is implemented for `Color`, we can use `TryInto`. 55 | let c2: Result = [183, 65, 14].try_into(); 56 | println!("{c2:?}"); 57 | 58 | let v = vec![183, 65, 14]; 59 | // With slice we should use the `try_from` function 60 | let c3 = Color::try_from(&v[..]); 61 | println!("{c3:?}"); 62 | // or put the slice within round brackets and use `try_into`. 63 | let c4: Result = (&v[..]).try_into(); 64 | println!("{c4:?}"); 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | use IntoColorError::*; 71 | 72 | #[test] 73 | fn test_tuple_out_of_range_positive() { 74 | assert_eq!(Color::try_from((256, 1000, 10000)), Err(IntConversion)); 75 | } 76 | 77 | #[test] 78 | fn test_tuple_out_of_range_negative() { 79 | assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion)); 80 | } 81 | 82 | #[test] 83 | fn test_tuple_sum() { 84 | assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion)); 85 | } 86 | 87 | #[test] 88 | fn test_tuple_correct() { 89 | let c: Result = (183, 65, 14).try_into(); 90 | assert!(c.is_ok()); 91 | assert_eq!( 92 | c.unwrap(), 93 | Color { 94 | red: 183, 95 | green: 65, 96 | blue: 14, 97 | } 98 | ); 99 | } 100 | 101 | #[test] 102 | fn test_array_out_of_range_positive() { 103 | let c: Result = [1000, 10000, 256].try_into(); 104 | assert_eq!(c, Err(IntConversion)); 105 | } 106 | 107 | #[test] 108 | fn test_array_out_of_range_negative() { 109 | let c: Result = [-10, -256, -1].try_into(); 110 | assert_eq!(c, Err(IntConversion)); 111 | } 112 | 113 | #[test] 114 | fn test_array_sum() { 115 | let c: Result = [-1, 255, 255].try_into(); 116 | assert_eq!(c, Err(IntConversion)); 117 | } 118 | 119 | #[test] 120 | fn test_array_correct() { 121 | let c: Result = [183, 65, 14].try_into(); 122 | assert!(c.is_ok()); 123 | assert_eq!( 124 | c.unwrap(), 125 | Color { 126 | red: 183, 127 | green: 65, 128 | blue: 14 129 | } 130 | ); 131 | } 132 | 133 | #[test] 134 | fn test_slice_out_of_range_positive() { 135 | let arr = [10000, 256, 1000]; 136 | assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); 137 | } 138 | 139 | #[test] 140 | fn test_slice_out_of_range_negative() { 141 | let arr = [-256, -1, -10]; 142 | assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); 143 | } 144 | 145 | #[test] 146 | fn test_slice_sum() { 147 | let arr = [-1, 255, 255]; 148 | assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); 149 | } 150 | 151 | #[test] 152 | fn test_slice_correct() { 153 | let v = vec![183, 65, 14]; 154 | let c: Result = Color::try_from(&v[..]); 155 | assert!(c.is_ok()); 156 | assert_eq!( 157 | c.unwrap(), 158 | Color { 159 | red: 183, 160 | green: 65, 161 | blue: 14, 162 | } 163 | ); 164 | } 165 | 166 | #[test] 167 | fn test_slice_excess_length() { 168 | let v = vec![0, 0, 0, 0]; 169 | assert_eq!(Color::try_from(&v[..]), Err(BadLen)); 170 | } 171 | 172 | #[test] 173 | fn test_slice_insufficient_length() { 174 | let v = vec![0, 0]; 175 | assert_eq!(Color::try_from(&v[..]), Err(BadLen)); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /exercises/23_conversions/using_as.rs: -------------------------------------------------------------------------------- 1 | // Type casting in Rust is done via the usage of the `as` operator. 2 | // Note that the `as` operator is not only used when type casting. It also helps 3 | // with renaming imports. 4 | 5 | fn average(values: &[f64]) -> f64 { 6 | let total = values.iter().sum::(); 7 | // TODO: Make a conversion before dividing. 8 | total / values.len() 9 | } 10 | 11 | fn main() { 12 | let values = [3.5, 0.3, 13.0, 11.7]; 13 | println!("{}", average(&values)); 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use super::*; 19 | 20 | #[test] 21 | fn returns_proper_type_and_value() { 22 | assert_eq!(average(&[3.5, 0.3, 13.0, 11.7]), 7.125); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /exercises/README.md: -------------------------------------------------------------------------------- 1 | # Exercise to Book Chapter mapping 2 | 3 | | Exercise | Book Chapter | 4 | | ---------------------- | ------------------- | 5 | | variables | §3.1 | 6 | | functions | §3.3 | 7 | | if | §3.5 | 8 | | primitive_types | §3.2, §4.3 | 9 | | vecs | §8.1 | 10 | | move_semantics | §4.1-2 | 11 | | structs | §5.1, §5.3 | 12 | | enums | §6, §18.3 | 13 | | strings | §8.2 | 14 | | modules | §7 | 15 | | hashmaps | §8.3 | 16 | | options | §10.1 | 17 | | error_handling | §9 | 18 | | generics | §10 | 19 | | traits | §10.2 | 20 | | lifetimes | §10.3 | 21 | | tests | §11.1 | 22 | | iterators | §13.2-4 | 23 | | smart_pointers | §15, §16.3 | 24 | | threads | §16.1-3 | 25 | | macros | §19.5 | 26 | | clippy | §21.4 | 27 | | conversions | n/a | 28 | -------------------------------------------------------------------------------- /exercises/quizzes/README.md: -------------------------------------------------------------------------------- 1 | # Quizzes 2 | 3 | After every couple of sections, there will be a quiz in this directory that'll test your knowledge on a bunch of sections at once. 4 | -------------------------------------------------------------------------------- /exercises/quizzes/quiz1.rs: -------------------------------------------------------------------------------- 1 | // This is a quiz for the following sections: 2 | // - Variables 3 | // - Functions 4 | // - If 5 | // 6 | // Mary is buying apples. The price of an apple is calculated as follows: 7 | // - An apple costs 2 rustbucks. 8 | // - However, if Mary buys more than 40 apples, the price of each apple in the 9 | // entire order is reduced to only 1 rustbuck! 10 | 11 | // TODO: Write a function that calculates the price of an order of apples given 12 | // the quantity bought. 13 | // fn calculate_price_of_apples(???) -> ??? { ??? } 14 | 15 | fn main() { 16 | // You can optionally experiment here. 17 | } 18 | 19 | fn calculate_price_of_apples(count_apples: i32) -> i32 { 20 | if count_apples <= 40 { 21 | return count_apples * 2 22 | } 23 | 24 | count_apples 25 | } 26 | 27 | // Don't change the tests! 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn verify_test() { 34 | assert_eq!(calculate_price_of_apples(35), 70); 35 | assert_eq!(calculate_price_of_apples(40), 80); 36 | assert_eq!(calculate_price_of_apples(41), 41); 37 | assert_eq!(calculate_price_of_apples(65), 65); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /exercises/quizzes/quiz2.rs: -------------------------------------------------------------------------------- 1 | // This is a quiz for the following sections: 2 | // - Strings 3 | // - Vecs 4 | // - Move semantics 5 | // - Modules 6 | // - Enums 7 | // 8 | // Let's build a little machine in the form of a function. As input, we're going 9 | // to give a list of strings and commands. These commands determine what action 10 | // is going to be applied to the string. It can either be: 11 | // - Uppercase the string 12 | // - Trim the string 13 | // - Append "bar" to the string a specified amount of times 14 | // 15 | // The exact form of this will be: 16 | // - The input is going to be a Vector of 2-length tuples, 17 | // the first element is the string, the second one is the command. 18 | // - The output element is going to be a vector of strings. 19 | 20 | enum Command { 21 | Uppercase, 22 | Trim, 23 | Append(usize), 24 | } 25 | 26 | mod my_module { 27 | use super::Command; 28 | 29 | // TODO: Complete the function as described above. 30 | // pub fn transformer(input: ???) -> ??? { ??? } 31 | } 32 | 33 | fn main() { 34 | // You can optionally experiment here. 35 | } 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | // TODO: What do we need to import to have `transformer` in scope? 40 | // use ???; 41 | use super::Command; 42 | 43 | #[test] 44 | fn it_works() { 45 | let input = vec![ 46 | ("hello".to_string(), Command::Uppercase), 47 | (" all roads lead to rome! ".to_string(), Command::Trim), 48 | ("foo".to_string(), Command::Append(1)), 49 | ("bar".to_string(), Command::Append(5)), 50 | ]; 51 | let output = transformer(input); 52 | 53 | assert_eq!( 54 | output, 55 | [ 56 | "HELLO", 57 | "all roads lead to rome!", 58 | "foobar", 59 | "barbarbarbarbarbar", 60 | ] 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /exercises/quizzes/quiz3.rs: -------------------------------------------------------------------------------- 1 | // This quiz tests: 2 | // - Generics 3 | // - Traits 4 | // 5 | // An imaginary magical school has a new report card generation system written 6 | // in Rust! Currently, the system only supports creating report cards where the 7 | // student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the 8 | // school also issues alphabetical grades (A+ -> F-) and needs to be able to 9 | // print both types of report card! 10 | // 11 | // Make the necessary code changes in the struct `ReportCard` and the impl 12 | // block to support alphabetical report cards in addition to numerical ones. 13 | 14 | // TODO: Adjust the struct as described above. 15 | struct ReportCard { 16 | grade: f32, 17 | student_name: String, 18 | student_age: u8, 19 | } 20 | 21 | // TODO: Adjust the impl block as described above. 22 | impl ReportCard { 23 | fn print(&self) -> String { 24 | format!( 25 | "{} ({}) - achieved a grade of {}", 26 | &self.student_name, &self.student_age, &self.grade, 27 | ) 28 | } 29 | } 30 | 31 | fn main() { 32 | // You can optionally experiment here. 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | 39 | #[test] 40 | fn generate_numeric_report_card() { 41 | let report_card = ReportCard { 42 | grade: 2.1, 43 | student_name: "Tom Wriggle".to_string(), 44 | student_age: 12, 45 | }; 46 | assert_eq!( 47 | report_card.print(), 48 | "Tom Wriggle (12) - achieved a grade of 2.1", 49 | ); 50 | } 51 | 52 | #[test] 53 | fn generate_alphabetic_report_card() { 54 | let report_card = ReportCard { 55 | grade: "A+", 56 | student_name: "Gary Plotter".to_string(), 57 | student_age: 11, 58 | }; 59 | assert_eq!( 60 | report_card.print(), 61 | "Gary Plotter (11) - achieved a grade of A+", 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /rust-analyzer.toml: -------------------------------------------------------------------------------- 1 | check.command = "clippy" 2 | check.extraArgs = ["--profile", "test"] 3 | -------------------------------------------------------------------------------- /solutions/00_intro/intro1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Congratulations, you finished the first exercise 🎉 3 | // As an introduction to Rustlings, the first exercise only required 4 | // entering `n` in the terminal to go to the next exercise. 5 | } 6 | -------------------------------------------------------------------------------- /solutions/00_intro/intro2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // `println!` instead of `printline!`. 3 | println!("Hello world!"); 4 | } 5 | -------------------------------------------------------------------------------- /solutions/01_variables/variables1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Declaring variables requires the `let` keyword. 3 | let x = 5; 4 | 5 | println!("x has the value {x}"); 6 | } 7 | -------------------------------------------------------------------------------- /solutions/01_variables/variables2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // The easiest way to fix the compiler error is to initialize the 3 | // variable `x`. By setting its value to an integer, Rust infers its type 4 | // as `i32` which is the default type for integers. 5 | let x = 42; 6 | 7 | // But we can enforce a type different from the default `i32` by adding 8 | // a type annotation: 9 | // let x: u8 = 42; 10 | 11 | if x == 10 { 12 | println!("x is ten!"); 13 | } else { 14 | println!("x is not ten!"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /solutions/01_variables/variables3.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_late_init)] 2 | 3 | fn main() { 4 | // Reading uninitialized variables isn't allowed in Rust! 5 | // Therefore, we need to assign a value first. 6 | let x: i32 = 42; 7 | 8 | println!("Number {x}"); 9 | 10 | // It is possible to declare a variable and initialize it later. 11 | // But it can't be used before initialization. 12 | let y: i32; 13 | y = 42; 14 | println!("Number {y}"); 15 | } 16 | -------------------------------------------------------------------------------- /solutions/01_variables/variables4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // In Rust, variables are immutable by default. 3 | // Adding the `mut` keyword after `let` makes the declared variable mutable. 4 | let mut x = 3; 5 | println!("Number {x}"); 6 | 7 | x = 5; 8 | println!("Number {x}"); 9 | } 10 | -------------------------------------------------------------------------------- /solutions/01_variables/variables5.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let number = "T-H-R-E-E"; 3 | println!("Spell a number: {}", number); 4 | 5 | // Using variable shadowing 6 | // https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing 7 | let number = 3; 8 | println!("Number plus two is: {}", number + 2); 9 | } 10 | -------------------------------------------------------------------------------- /solutions/01_variables/variables6.rs: -------------------------------------------------------------------------------- 1 | // The type of constants must always be annotated. 2 | const NUMBER: u64 = 3; 3 | 4 | fn main() { 5 | println!("Number: {NUMBER}"); 6 | } 7 | -------------------------------------------------------------------------------- /solutions/02_functions/functions1.rs: -------------------------------------------------------------------------------- 1 | // Some function with the name `call_me` without arguments or a return value. 2 | fn call_me() { 3 | println!("Hello world!"); 4 | } 5 | 6 | fn main() { 7 | call_me(); 8 | } 9 | -------------------------------------------------------------------------------- /solutions/02_functions/functions2.rs: -------------------------------------------------------------------------------- 1 | // The type of function arguments must be annotated. 2 | // Added the type annotation `u64`. 3 | fn call_me(num: u64) { 4 | for i in 0..num { 5 | println!("Ring! Call number {}", i + 1); 6 | } 7 | } 8 | 9 | fn main() { 10 | call_me(3); 11 | } 12 | -------------------------------------------------------------------------------- /solutions/02_functions/functions3.rs: -------------------------------------------------------------------------------- 1 | fn call_me(num: u8) { 2 | for i in 0..num { 3 | println!("Ring! Call number {}", i + 1); 4 | } 5 | } 6 | 7 | fn main() { 8 | // `call_me` expects an argument. 9 | call_me(5); 10 | } 11 | -------------------------------------------------------------------------------- /solutions/02_functions/functions4.rs: -------------------------------------------------------------------------------- 1 | fn is_even(num: i64) -> bool { 2 | num % 2 == 0 3 | } 4 | 5 | // The return type must always be annotated. 6 | fn sale_price(price: i64) -> i64 { 7 | if is_even(price) { 8 | price - 10 9 | } else { 10 | price - 3 11 | } 12 | } 13 | 14 | fn main() { 15 | let original_price = 51; 16 | println!("Your sale price is {}", sale_price(original_price)); 17 | } 18 | -------------------------------------------------------------------------------- /solutions/02_functions/functions5.rs: -------------------------------------------------------------------------------- 1 | fn square(num: i32) -> i32 { 2 | // Removed the semicolon `;` at the end of the line below to implicitly return the result. 3 | num * num 4 | } 5 | 6 | fn main() { 7 | let answer = square(3); 8 | println!("The square of 3 is {answer}"); 9 | } 10 | -------------------------------------------------------------------------------- /solutions/03_if/if1.rs: -------------------------------------------------------------------------------- 1 | fn bigger(a: i32, b: i32) -> i32 { 2 | if a > b { 3 | a 4 | } else { 5 | b 6 | } 7 | } 8 | 9 | fn main() { 10 | // You can optionally experiment here. 11 | } 12 | 13 | // Don't mind this for now :) 14 | #[cfg(test)] 15 | mod tests { 16 | use super::*; 17 | 18 | #[test] 19 | fn ten_is_bigger_than_eight() { 20 | assert_eq!(10, bigger(10, 8)); 21 | } 22 | 23 | #[test] 24 | fn fortytwo_is_bigger_than_thirtytwo() { 25 | assert_eq!(42, bigger(32, 42)); 26 | } 27 | 28 | #[test] 29 | fn equal_numbers() { 30 | assert_eq!(42, bigger(42, 42)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /solutions/03_if/if2.rs: -------------------------------------------------------------------------------- 1 | fn picky_eater(food: &str) -> &str { 2 | if food == "strawberry" { 3 | "Yummy!" 4 | } else if food == "potato" { 5 | "I guess I can eat that." 6 | } else { 7 | "No thanks!" 8 | } 9 | } 10 | 11 | fn main() { 12 | // You can optionally experiment here. 13 | } 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use super::*; 18 | 19 | #[test] 20 | fn yummy_food() { 21 | assert_eq!(picky_eater("strawberry"), "Yummy!"); 22 | } 23 | 24 | #[test] 25 | fn neutral_food() { 26 | assert_eq!(picky_eater("potato"), "I guess I can eat that."); 27 | } 28 | 29 | #[test] 30 | fn default_disliked_food() { 31 | assert_eq!(picky_eater("broccoli"), "No thanks!"); 32 | assert_eq!(picky_eater("gummy bears"), "No thanks!"); 33 | assert_eq!(picky_eater("literally anything"), "No thanks!"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /solutions/03_if/if3.rs: -------------------------------------------------------------------------------- 1 | fn animal_habitat(animal: &str) -> &str { 2 | let identifier = if animal == "crab" { 3 | 1 4 | } else if animal == "gopher" { 5 | 2 6 | } else if animal == "snake" { 7 | 3 8 | } else { 9 | // Any unused identifier. 10 | 4 11 | }; 12 | 13 | // Instead of such an identifier, you would use an enum in Rust. 14 | // But we didn't get into enums yet. 15 | if identifier == 1 { 16 | "Beach" 17 | } else if identifier == 2 { 18 | "Burrow" 19 | } else if identifier == 3 { 20 | "Desert" 21 | } else { 22 | "Unknown" 23 | } 24 | } 25 | 26 | fn main() { 27 | // You can optionally experiment here. 28 | } 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | use super::*; 33 | 34 | #[test] 35 | fn gopher_lives_in_burrow() { 36 | assert_eq!(animal_habitat("gopher"), "Burrow") 37 | } 38 | 39 | #[test] 40 | fn snake_lives_in_desert() { 41 | assert_eq!(animal_habitat("snake"), "Desert") 42 | } 43 | 44 | #[test] 45 | fn crab_lives_on_beach() { 46 | assert_eq!(animal_habitat("crab"), "Beach") 47 | } 48 | 49 | #[test] 50 | fn unknown_animal() { 51 | assert_eq!(animal_habitat("dinosaur"), "Unknown") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /solutions/04_primitive_types/primitive_types1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let is_morning = true; 3 | if is_morning { 4 | println!("Good morning!"); 5 | } 6 | 7 | let is_evening = !is_morning; 8 | if is_evening { 9 | println!("Good evening!"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/04_primitive_types/primitive_types2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let my_first_initial = 'C'; 3 | if my_first_initial.is_alphabetic() { 4 | println!("Alphabetical!"); 5 | } else if my_first_initial.is_numeric() { 6 | println!("Numerical!"); 7 | } else { 8 | println!("Neither alphabetic nor numeric!"); 9 | } 10 | 11 | // Example with an emoji. 12 | let your_character = '🦀'; 13 | 14 | if your_character.is_alphabetic() { 15 | println!("Alphabetical!"); 16 | } else if your_character.is_numeric() { 17 | println!("Numerical!"); 18 | } else { 19 | println!("Neither alphabetic nor numeric!"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/04_primitive_types/primitive_types3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // An array with 100 elements of the value 42. 3 | let a = [42; 100]; 4 | 5 | if a.len() >= 100 { 6 | println!("Wow, that's a big array!"); 7 | } else { 8 | println!("Meh, I eat arrays like that for breakfast."); 9 | panic!("Array not big enough, more elements needed"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/04_primitive_types/primitive_types4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // You can optionally experiment here. 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | #[test] 8 | fn slice_out_of_array() { 9 | let a = [1, 2, 3, 4, 5]; 10 | // 0 1 2 3 4 <- indices 11 | // ------- 12 | // | 13 | // +--- slice 14 | 15 | // Note that the upper index 4 is excluded. 16 | let nice_slice = &a[1..4]; 17 | assert_eq!([2, 3, 4], nice_slice); 18 | 19 | // The upper index can be included by using the syntax `..=` (with `=` sign) 20 | let nice_slice = &a[1..=3]; 21 | assert_eq!([2, 3, 4], nice_slice); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /solutions/04_primitive_types/primitive_types5.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let cat = ("Furry McFurson", 3.5); 3 | 4 | // Destructuring the tuple. 5 | let (name, age) = cat; 6 | 7 | println!("{name} is {age} years old"); 8 | } 9 | -------------------------------------------------------------------------------- /solutions/04_primitive_types/primitive_types6.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // You can optionally experiment here. 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | #[test] 8 | fn indexing_tuple() { 9 | let numbers = (1, 2, 3); 10 | 11 | // Tuple indexing syntax. 12 | let second = numbers.1; 13 | 14 | assert_eq!(second, 2, "This is not the 2nd number in the tuple!"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /solutions/05_vecs/vecs1.rs: -------------------------------------------------------------------------------- 1 | fn array_and_vec() -> ([i32; 4], Vec) { 2 | let a = [10, 20, 30, 40]; // Array 3 | 4 | // Used the `vec!` macro. 5 | let v = vec![10, 20, 30, 40]; 6 | 7 | (a, v) 8 | } 9 | 10 | fn main() { 11 | // You can optionally experiment here. 12 | } 13 | 14 | #[cfg(test)] 15 | mod tests { 16 | use super::*; 17 | 18 | #[test] 19 | fn test_array_and_vec_similarity() { 20 | let (a, v) = array_and_vec(); 21 | assert_eq!(a, *v); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /solutions/05_vecs/vecs2.rs: -------------------------------------------------------------------------------- 1 | fn vec_loop(input: &[i32]) -> Vec { 2 | let mut output = Vec::new(); 3 | 4 | for element in input { 5 | output.push(2 * element); 6 | } 7 | 8 | output 9 | } 10 | 11 | fn vec_map_example(input: &[i32]) -> Vec { 12 | // An example of collecting a vector after mapping. 13 | // We map each element of the `input` slice to its value plus 1. 14 | // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`. 15 | input.iter().map(|element| element + 1).collect() 16 | } 17 | 18 | fn vec_map(input: &[i32]) -> Vec { 19 | // We will dive deeper into iterators, but for now, this is all what you 20 | // had to do! 21 | // Advanced note: This method is more efficient because it automatically 22 | // preallocates enough capacity. This can be done manually in `vec_loop` 23 | // using `Vec::with_capacity(input.len())` instead of `Vec::new()`. 24 | input.iter().map(|element| 2 * element).collect() 25 | } 26 | 27 | fn main() { 28 | // You can optionally experiment here. 29 | } 30 | 31 | #[cfg(test)] 32 | mod tests { 33 | use super::*; 34 | 35 | #[test] 36 | fn test_vec_loop() { 37 | let input = [2, 4, 6, 8, 10]; 38 | let ans = vec_loop(&input); 39 | assert_eq!(ans, [4, 8, 12, 16, 20]); 40 | } 41 | 42 | #[test] 43 | fn test_vec_map_example() { 44 | let input = [1, 2, 3]; 45 | let ans = vec_map_example(&input); 46 | assert_eq!(ans, [2, 3, 4]); 47 | } 48 | 49 | #[test] 50 | fn test_vec_map() { 51 | let input = [2, 4, 6, 8, 10]; 52 | let ans = vec_map(&input); 53 | assert_eq!(ans, [4, 8, 12, 16, 20]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /solutions/06_move_semantics/move_semantics1.rs: -------------------------------------------------------------------------------- 1 | fn fill_vec(vec: Vec) -> Vec { 2 | let mut vec = vec; 3 | // ^^^ added 4 | 5 | vec.push(88); 6 | 7 | vec 8 | } 9 | 10 | fn main() { 11 | // You can optionally experiment here. 12 | } 13 | 14 | #[cfg(test)] 15 | mod tests { 16 | use super::*; 17 | 18 | #[test] 19 | fn move_semantics1() { 20 | let vec0 = vec![22, 44, 66]; 21 | let vec1 = fill_vec(vec0); 22 | // `vec0` can't be accessed anymore because it is moved to `fill_vec`. 23 | assert_eq!(vec1, vec![22, 44, 66, 88]); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/06_move_semantics/move_semantics2.rs: -------------------------------------------------------------------------------- 1 | fn fill_vec(vec: Vec) -> Vec { 2 | let mut vec = vec; 3 | 4 | vec.push(88); 5 | 6 | vec 7 | } 8 | 9 | fn main() { 10 | // You can optionally experiment here. 11 | } 12 | 13 | #[cfg(test)] 14 | mod tests { 15 | use super::*; 16 | 17 | #[test] 18 | fn move_semantics2() { 19 | let vec0 = vec![22, 44, 66]; 20 | 21 | // Cloning `vec0` so that the clone is moved into `fill_vec`, not `vec0` 22 | // itself. 23 | let vec1 = fill_vec(vec0.clone()); 24 | 25 | assert_eq!(vec0, [22, 44, 66]); 26 | assert_eq!(vec1, [22, 44, 66, 88]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /solutions/06_move_semantics/move_semantics3.rs: -------------------------------------------------------------------------------- 1 | fn fill_vec(mut vec: Vec) -> Vec { 2 | // ^^^ added 3 | vec.push(88); 4 | 5 | vec 6 | } 7 | 8 | fn main() { 9 | // You can optionally experiment here. 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use super::*; 15 | 16 | #[test] 17 | fn move_semantics3() { 18 | let vec0 = vec![22, 44, 66]; 19 | let vec1 = fill_vec(vec0); 20 | assert_eq!(vec1, [22, 44, 66, 88]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /solutions/06_move_semantics/move_semantics4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // You can optionally experiment here. 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | // TODO: Fix the compiler errors only by reordering the lines in the test. 8 | // Don't add, change or remove any line. 9 | #[test] 10 | fn move_semantics4() { 11 | let mut x = Vec::new(); 12 | let y = &mut x; 13 | // `y` used here. 14 | y.push(42); 15 | // The mutable reference `y` is not used anymore, 16 | // therefore a new reference can be created. 17 | let z = &mut x; 18 | z.push(13); 19 | assert_eq!(x, [42, 13]); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/06_move_semantics/move_semantics5.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::ptr_arg)] 2 | 3 | // Borrows instead of taking ownership. 4 | // It is recommended to use `&str` instead of `&String` here. But this is 5 | // enough for now because we didn't handle strings yet. 6 | fn get_char(data: &String) -> char { 7 | data.chars().last().unwrap() 8 | } 9 | 10 | // Takes ownership instead of borrowing. 11 | fn string_uppercase(mut data: String) { 12 | data = data.to_uppercase(); 13 | 14 | println!("{data}"); 15 | } 16 | 17 | fn main() { 18 | let data = "Rust is great!".to_string(); 19 | 20 | get_char(&data); 21 | 22 | string_uppercase(data); 23 | } 24 | -------------------------------------------------------------------------------- /solutions/07_structs/structs1.rs: -------------------------------------------------------------------------------- 1 | struct ColorRegularStruct { 2 | red: u8, 3 | green: u8, 4 | blue: u8, 5 | } 6 | 7 | struct ColorTupleStruct(u8, u8, u8); 8 | 9 | #[derive(Debug)] 10 | struct UnitStruct; 11 | 12 | fn main() { 13 | // You can optionally experiment here. 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use super::*; 19 | 20 | #[test] 21 | fn regular_structs() { 22 | let green = ColorRegularStruct { 23 | red: 0, 24 | green: 255, 25 | blue: 0, 26 | }; 27 | 28 | assert_eq!(green.red, 0); 29 | assert_eq!(green.green, 255); 30 | assert_eq!(green.blue, 0); 31 | } 32 | 33 | #[test] 34 | fn tuple_structs() { 35 | let green = ColorTupleStruct(0, 255, 0); 36 | 37 | assert_eq!(green.0, 0); 38 | assert_eq!(green.1, 255); 39 | assert_eq!(green.2, 0); 40 | } 41 | 42 | #[test] 43 | fn unit_structs() { 44 | let unit_struct = UnitStruct; 45 | let message = format!("{unit_struct:?}s are fun!"); 46 | 47 | assert_eq!(message, "UnitStructs are fun!"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /solutions/07_structs/structs2.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct Order { 3 | name: String, 4 | year: u32, 5 | made_by_phone: bool, 6 | made_by_mobile: bool, 7 | made_by_email: bool, 8 | item_number: u32, 9 | count: u32, 10 | } 11 | 12 | fn create_order_template() -> Order { 13 | Order { 14 | name: String::from("Bob"), 15 | year: 2019, 16 | made_by_phone: false, 17 | made_by_mobile: false, 18 | made_by_email: true, 19 | item_number: 123, 20 | count: 0, 21 | } 22 | } 23 | 24 | fn main() { 25 | // You can optionally experiment here. 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn your_order() { 34 | let order_template = create_order_template(); 35 | 36 | let your_order = Order { 37 | name: String::from("Hacker in Rust"), 38 | count: 1, 39 | // Struct update syntax 40 | ..order_template 41 | }; 42 | 43 | assert_eq!(your_order.name, "Hacker in Rust"); 44 | assert_eq!(your_order.year, order_template.year); 45 | assert_eq!(your_order.made_by_phone, order_template.made_by_phone); 46 | assert_eq!(your_order.made_by_mobile, order_template.made_by_mobile); 47 | assert_eq!(your_order.made_by_email, order_template.made_by_email); 48 | assert_eq!(your_order.item_number, order_template.item_number); 49 | assert_eq!(your_order.count, 1); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /solutions/07_structs/structs3.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct Package { 3 | sender_country: String, 4 | recipient_country: String, 5 | weight_in_grams: u32, 6 | } 7 | 8 | impl Package { 9 | fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self { 10 | if weight_in_grams < 10 { 11 | // This isn't how you should handle errors in Rust, but we will 12 | // learn about error handling later. 13 | panic!("Can't ship a package with weight below 10 grams"); 14 | } 15 | 16 | Self { 17 | sender_country, 18 | recipient_country, 19 | weight_in_grams, 20 | } 21 | } 22 | 23 | fn is_international(&self) -> bool { 24 | // ^^^^^^^ added 25 | self.sender_country != self.recipient_country 26 | } 27 | 28 | fn get_fees(&self, cents_per_gram: u32) -> u32 { 29 | // ^^^^^^ added 30 | self.weight_in_grams * cents_per_gram 31 | } 32 | } 33 | 34 | fn main() { 35 | // You can optionally experiment here. 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use super::*; 41 | 42 | #[test] 43 | #[should_panic] 44 | fn fail_creating_weightless_package() { 45 | let sender_country = String::from("Spain"); 46 | let recipient_country = String::from("Austria"); 47 | 48 | Package::new(sender_country, recipient_country, 5); 49 | } 50 | 51 | #[test] 52 | fn create_international_package() { 53 | let sender_country = String::from("Spain"); 54 | let recipient_country = String::from("Russia"); 55 | 56 | let package = Package::new(sender_country, recipient_country, 1200); 57 | 58 | assert!(package.is_international()); 59 | } 60 | 61 | #[test] 62 | fn create_local_package() { 63 | let sender_country = String::from("Canada"); 64 | let recipient_country = sender_country.clone(); 65 | 66 | let package = Package::new(sender_country, recipient_country, 1200); 67 | 68 | assert!(!package.is_international()); 69 | } 70 | 71 | #[test] 72 | fn calculate_transport_fees() { 73 | let sender_country = String::from("Spain"); 74 | let recipient_country = String::from("Spain"); 75 | 76 | let cents_per_gram = 3; 77 | 78 | let package = Package::new(sender_country, recipient_country, 1500); 79 | 80 | assert_eq!(package.get_fees(cents_per_gram), 4500); 81 | assert_eq!(package.get_fees(cents_per_gram * 2), 9000); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /solutions/08_enums/enums1.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | enum Message { 3 | Resize, 4 | Move, 5 | Echo, 6 | ChangeColor, 7 | Quit, 8 | } 9 | 10 | fn main() { 11 | println!("{:?}", Message::Resize); 12 | println!("{:?}", Message::Move); 13 | println!("{:?}", Message::Echo); 14 | println!("{:?}", Message::ChangeColor); 15 | println!("{:?}", Message::Quit); 16 | } 17 | -------------------------------------------------------------------------------- /solutions/08_enums/enums2.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct Point { 3 | x: u64, 4 | y: u64, 5 | } 6 | 7 | #[derive(Debug)] 8 | enum Message { 9 | Resize { width: u64, height: u64 }, 10 | Move(Point), 11 | Echo(String), 12 | ChangeColor(u8, u8, u8), 13 | Quit, 14 | } 15 | 16 | impl Message { 17 | fn call(&self) { 18 | println!("{self:?}"); 19 | } 20 | } 21 | 22 | fn main() { 23 | let messages = [ 24 | Message::Resize { 25 | width: 10, 26 | height: 30, 27 | }, 28 | Message::Move(Point { x: 10, y: 15 }), 29 | Message::Echo(String::from("hello world")), 30 | Message::ChangeColor(200, 255, 255), 31 | Message::Quit, 32 | ]; 33 | 34 | for message in &messages { 35 | message.call(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /solutions/08_enums/enums3.rs: -------------------------------------------------------------------------------- 1 | struct Point { 2 | x: u64, 3 | y: u64, 4 | } 5 | 6 | enum Message { 7 | Resize { width: u64, height: u64 }, 8 | Move(Point), 9 | Echo(String), 10 | ChangeColor(u8, u8, u8), 11 | Quit, 12 | } 13 | 14 | struct State { 15 | width: u64, 16 | height: u64, 17 | position: Point, 18 | message: String, 19 | color: (u8, u8, u8), 20 | quit: bool, 21 | } 22 | 23 | impl State { 24 | fn resize(&mut self, width: u64, height: u64) { 25 | self.width = width; 26 | self.height = height; 27 | } 28 | 29 | fn move_position(&mut self, point: Point) { 30 | self.position = point; 31 | } 32 | 33 | fn echo(&mut self, s: String) { 34 | self.message = s; 35 | } 36 | 37 | fn change_color(&mut self, red: u8, green: u8, blue: u8) { 38 | self.color = (red, green, blue); 39 | } 40 | 41 | fn quit(&mut self) { 42 | self.quit = true; 43 | } 44 | 45 | fn process(&mut self, message: Message) { 46 | match message { 47 | Message::Resize { width, height } => self.resize(width, height), 48 | Message::Move(point) => self.move_position(point), 49 | Message::Echo(string) => self.echo(string), 50 | Message::ChangeColor(red, green, blue) => self.change_color(red, green, blue), 51 | Message::Quit => self.quit(), 52 | } 53 | } 54 | } 55 | 56 | fn main() { 57 | // You can optionally experiment here. 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use super::*; 63 | 64 | #[test] 65 | fn test_match_message_call() { 66 | let mut state = State { 67 | width: 0, 68 | height: 0, 69 | position: Point { x: 0, y: 0 }, 70 | message: String::from("hello world"), 71 | color: (0, 0, 0), 72 | quit: false, 73 | }; 74 | 75 | state.process(Message::Resize { 76 | width: 10, 77 | height: 30, 78 | }); 79 | state.process(Message::Move(Point { x: 10, y: 15 })); 80 | state.process(Message::Echo(String::from("Hello world!"))); 81 | state.process(Message::ChangeColor(255, 0, 255)); 82 | state.process(Message::Quit); 83 | 84 | assert_eq!(state.width, 10); 85 | assert_eq!(state.height, 30); 86 | assert_eq!(state.position.x, 10); 87 | assert_eq!(state.position.y, 15); 88 | assert_eq!(state.message, "Hello world!"); 89 | assert_eq!(state.color, (255, 0, 255)); 90 | assert!(state.quit); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /solutions/09_strings/strings1.rs: -------------------------------------------------------------------------------- 1 | fn current_favorite_color() -> String { 2 | // Equivalent to `String::from("blue")` 3 | "blue".to_string() 4 | } 5 | 6 | fn main() { 7 | let answer = current_favorite_color(); 8 | println!("My current favorite color is {answer}"); 9 | } 10 | -------------------------------------------------------------------------------- /solutions/09_strings/strings2.rs: -------------------------------------------------------------------------------- 1 | fn is_a_color_word(attempt: &str) -> bool { 2 | attempt == "green" || attempt == "blue" || attempt == "red" 3 | } 4 | 5 | fn main() { 6 | let word = String::from("green"); 7 | 8 | if is_a_color_word(&word) { 9 | // ^ added to have `&String` which is automatically 10 | // coerced to `&str` by the compiler. 11 | println!("That is a color word I know!"); 12 | } else { 13 | println!("That is not a color word I know."); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/09_strings/strings3.rs: -------------------------------------------------------------------------------- 1 | fn trim_me(input: &str) -> &str { 2 | input.trim() 3 | } 4 | 5 | fn compose_me(input: &str) -> String { 6 | // The macro `format!` has the same syntax as `println!`, but it returns a 7 | // string instead of printing it to the terminal. 8 | // Equivalent to `input.to_string() + " world!"` 9 | format!("{input} world!") 10 | } 11 | 12 | fn replace_me(input: &str) -> String { 13 | input.replace("cars", "balloons") 14 | } 15 | 16 | fn main() { 17 | // You can optionally experiment here. 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use super::*; 23 | 24 | #[test] 25 | fn trim_a_string() { 26 | assert_eq!(trim_me("Hello! "), "Hello!"); 27 | assert_eq!(trim_me(" What's up!"), "What's up!"); 28 | assert_eq!(trim_me(" Hola! "), "Hola!"); 29 | } 30 | 31 | #[test] 32 | fn compose_a_string() { 33 | assert_eq!(compose_me("Hello"), "Hello world!"); 34 | assert_eq!(compose_me("Goodbye"), "Goodbye world!"); 35 | } 36 | 37 | #[test] 38 | fn replace_a_string() { 39 | assert_eq!( 40 | replace_me("I think cars are cool"), 41 | "I think balloons are cool", 42 | ); 43 | assert_eq!( 44 | replace_me("I love to look at cars"), 45 | "I love to look at balloons", 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /solutions/09_strings/strings4.rs: -------------------------------------------------------------------------------- 1 | fn string_slice(arg: &str) { 2 | println!("{arg}"); 3 | } 4 | 5 | fn string(arg: String) { 6 | println!("{arg}"); 7 | } 8 | 9 | fn main() { 10 | string_slice("blue"); 11 | 12 | string("red".to_string()); 13 | 14 | string(String::from("hi")); 15 | 16 | string("rust is fun!".to_owned()); 17 | 18 | // Here, both answers work. 19 | // `.into()` converts a type into an expected type. 20 | // If it is called where `String` is expected, it will convert `&str` to `String`. 21 | string("nice weather".into()); 22 | // But if it is called where `&str` is expected, then `&str` is kept `&str` since no conversion is needed. 23 | // If you remove the `#[allow(…)]` line, then Clippy will tell you to remove `.into()` below since it is a useless conversion. 24 | #[allow(clippy::useless_conversion)] 25 | string_slice("nice weather".into()); 26 | 27 | string(format!("Interpolation {}", "Station")); 28 | 29 | // WARNING: This is byte indexing, not character indexing. 30 | // Character indexing can be done using `s.chars().nth(INDEX)`. 31 | string_slice(&String::from("abc")[0..1]); 32 | 33 | string_slice(" hello there ".trim()); 34 | 35 | string("Happy Monday!".replace("Mon", "Tues")); 36 | 37 | string("mY sHiFt KeY iS sTiCkY".to_lowercase()); 38 | } 39 | -------------------------------------------------------------------------------- /solutions/10_modules/modules1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/10_modules/modules2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/10_modules/modules3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/11_hashmaps/hashmaps1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/11_hashmaps/hashmaps2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/11_hashmaps/hashmaps3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/12_options/options1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/12_options/options2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/12_options/options3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/13_error_handling/errors1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/13_error_handling/errors2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/13_error_handling/errors3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/13_error_handling/errors4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/13_error_handling/errors5.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/13_error_handling/errors6.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/14_generics/generics1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/14_generics/generics2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/15_traits/traits1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/15_traits/traits2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/15_traits/traits3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/15_traits/traits4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/15_traits/traits5.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/16_lifetimes/lifetimes1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/16_lifetimes/lifetimes2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/16_lifetimes/lifetimes3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/17_tests/tests1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/17_tests/tests2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/17_tests/tests3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/18_iterators/iterators1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/18_iterators/iterators2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/18_iterators/iterators3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/18_iterators/iterators4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/18_iterators/iterators5.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/19_smart_pointers/arc1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/19_smart_pointers/box1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/19_smart_pointers/cow1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/19_smart_pointers/rc1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/20_threads/threads1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/20_threads/threads2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/20_threads/threads3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/21_macros/macros1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/21_macros/macros2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/21_macros/macros3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/21_macros/macros4.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/22_clippy/clippy1.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/22_clippy/clippy2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/22_clippy/clippy3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/23_conversions/as_ref_mut.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/23_conversions/from_into.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/23_conversions/from_str.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/23_conversions/try_from_into.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/23_conversions/using_as.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/README.md: -------------------------------------------------------------------------------- 1 | # Official Rustlings solutions 2 | 3 | Before you finish an exercise, its solution file will only contain an empty `main` function. 4 | The content of this file will be automatically replaced by the actual solution once you finish the exercise. 5 | 6 | Note that these solutions are often only _one possibility_ to solve an exercise. 7 | -------------------------------------------------------------------------------- /solutions/quizzes/quiz1.rs: -------------------------------------------------------------------------------- 1 | // Mary is buying apples. The price of an apple is calculated as follows: 2 | // - An apple costs 2 rustbucks. 3 | // - However, if Mary buys more than 40 apples, the price of each apple in the 4 | // entire order is reduced to only 1 rustbuck! 5 | 6 | fn calculate_price_of_apples(n_apples: u64) -> u64 { 7 | if n_apples > 40 { 8 | n_apples 9 | } else { 10 | 2 * n_apples 11 | } 12 | } 13 | 14 | fn main() { 15 | // You can optionally experiment here. 16 | } 17 | 18 | // Don't change the tests! 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | 23 | #[test] 24 | fn verify_test() { 25 | assert_eq!(calculate_price_of_apples(35), 70); 26 | assert_eq!(calculate_price_of_apples(40), 80); 27 | assert_eq!(calculate_price_of_apples(41), 41); 28 | assert_eq!(calculate_price_of_apples(65), 65); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /solutions/quizzes/quiz2.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | -------------------------------------------------------------------------------- /solutions/quizzes/quiz3.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // DON'T EDIT THIS SOLUTION FILE! 3 | // It will be automatically filled after you finish the exercise. 4 | } 5 | --------------------------------------------------------------------------------