Compare commits

..

No commits in common. "main" and "v6.5.0" have entirely different histories.
main ... v6.5.0

14 changed files with 103 additions and 35 deletions

View File

@ -1,9 +1,5 @@
## Unreleased ## Unreleased
### Changed
- `vecs2`: Removed the use of `map` and `collect`, which are only taught later.
## 6.5.0 (2025-08-21) ## 6.5.0 (2025-08-21)
### Added ### Added

View File

@ -9,6 +9,26 @@ fn vec_loop(input: &[i32]) -> Vec<i32> {
output output
} }
fn vec_map_example(input: &[i32]) -> Vec<i32> {
// An example of collecting a vector after mapping.
// We map each element of the `input` slice to its value plus 1.
// If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`.
input.iter().map(|element| element + 1).collect()
}
fn vec_map(input: &[i32]) -> Vec<i32> {
// TODO: Here, we also want to multiply each element in the `input` slice
// by 2, but with iterator mapping instead of manually pushing into an empty
// vector.
// See the example in the function `vec_map_example` above.
input
.iter()
.map(|element| {
// ???
})
.collect()
}
fn main() { fn main() {
// You can optionally experiment here. // You can optionally experiment here.
} }
@ -23,4 +43,18 @@ mod tests {
let ans = vec_loop(&input); let ans = vec_loop(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]); assert_eq!(ans, [4, 8, 12, 16, 20]);
} }
#[test]
fn test_vec_map_example() {
let input = [1, 2, 3];
let ans = vec_map_example(&input);
assert_eq!(ans, [2, 3, 4]);
}
#[test]
fn test_vec_map() {
let input = [2, 4, 6, 8, 10];
let ans = vec_map(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
}
} }

View File

@ -1,6 +1,7 @@
// Here are some more easy Clippy fixes so you can see its utility. // Here are some more easy Clippy fixes so you can see its utility 📎
// TODO: Fix all the Clippy lints. // TODO: Fix all the Clippy lints.
#[rustfmt::skip]
#[allow(unused_variables, unused_assignments)] #[allow(unused_variables, unused_assignments)]
fn main() { fn main() {
let my_option: Option<&str> = None; let my_option: Option<&str> = None;
@ -10,16 +11,14 @@ fn main() {
println!("{}", my_option.unwrap()); println!("{}", my_option.unwrap());
} }
#[rustfmt::skip]
let my_arr = &[ let my_arr = &[
-1, -2, -3 -1, -2, -3
-4, -5, -6 -4, -5, -6
]; ];
println!("My array! Here it is: {my_arr:?}"); println!("My array! Here it is: {my_arr:?}");
let mut my_vec = vec![1, 2, 3, 4, 5]; let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
my_vec.resize(0, 5); println!("This Vec is empty, see? {my_empty_vec:?}");
println!("This Vec is empty, see? {my_vec:?}");
let mut value_a = 45; let mut value_a = 45;
let mut value_b = 66; let mut value_b = 66;

View File

@ -9,7 +9,7 @@
| vecs | §8.1 | | vecs | §8.1 |
| move_semantics | §4.1-2 | | move_semantics | §4.1-2 |
| structs | §5.1, §5.3 | | structs | §5.1, §5.3 |
| enums | §6, §19.3 | | enums | §6, §18.3 |
| strings | §8.2 | | strings | §8.2 |
| modules | §7 | | modules | §7 |
| hashmaps | §8.3 | | hashmaps | §8.3 |

View File

@ -318,7 +318,16 @@ of the Rust book to learn more."""
name = "vecs2" name = "vecs2"
dir = "05_vecs" dir = "05_vecs"
hint = """ hint = """
Use the `.push()` method on the vector to push new elements to it.""" In the first function, we create an empty vector and want to push new elements
to it.
In the second function, we map the values of the input and collect them into
a vector.
After you've completed both functions, decide for yourself which approach you
like better.
What do you think is the more commonly used pattern under Rust developers?"""
# MOVE SEMANTICS # MOVE SEMANTICS

View File

@ -8,6 +8,22 @@ fn vec_loop(input: &[i32]) -> Vec<i32> {
output output
} }
fn vec_map_example(input: &[i32]) -> Vec<i32> {
// An example of collecting a vector after mapping.
// We map each element of the `input` slice to its value plus 1.
// If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`.
input.iter().map(|element| element + 1).collect()
}
fn vec_map(input: &[i32]) -> Vec<i32> {
// We will dive deeper into iterators, but for now, this is all what you
// had to do!
// Advanced note: This method is more efficient because it automatically
// preallocates enough capacity. This can be done manually in `vec_loop`
// using `Vec::with_capacity(input.len())` instead of `Vec::new()`.
input.iter().map(|element| 2 * element).collect()
}
fn main() { fn main() {
// You can optionally experiment here. // You can optionally experiment here.
} }
@ -22,4 +38,18 @@ mod tests {
let ans = vec_loop(&input); let ans = vec_loop(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]); assert_eq!(ans, [4, 8, 12, 16, 20]);
} }
#[test]
fn test_vec_map_example() {
let input = [1, 2, 3];
let ans = vec_map_example(&input);
assert_eq!(ans, [2, 3, 4]);
}
#[test]
fn test_vec_map() {
let input = [2, 4, 6, 8, 10];
let ans = vec_map(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
}
} }

View File

@ -19,7 +19,7 @@ fn main() {
// `.into()` converts a type into an expected type. // `.into()` converts a type into an expected type.
// If it is called where `String` is expected, it will convert `&str` to `String`. // If it is called where `String` is expected, it will convert `&str` to `String`.
string("nice weather".into()); string("nice weather".into());
// But if it is called where `&str` is expected, then `&str` is kept as `&str` since no conversion is needed. // But if it is called where `&str` is expected, then `&str` is kept `&str` since no conversion is needed.
// If you remove the `#[allow(…)]` line, then Clippy will tell you to remove `.into()` below since it is a useless conversion. // If you remove the `#[allow(…)]` line, then Clippy will tell you to remove `.into()` below since it is a useless conversion.
#[allow(clippy::useless_conversion)] #[allow(clippy::useless_conversion)]
string_slice("nice weather".into()); string_slice("nice weather".into());

View File

@ -1,7 +1,7 @@
// A basket of fruits in the form of a hash map needs to be defined. The key // A basket of fruits in the form of a hash map needs to be defined. The key
// represents the name of the fruit and the value represents how many of that // represents the name of the fruit and the value represents how many of that
// particular fruit is in the basket. You have to put at least 3 different // particular fruit is in the basket. You have to put at least 3 different
// types of fruits (e.g. apple, banana, mango) in the basket and the total count // types of fruits (e.g apple, banana, mango) in the basket and the total count
// of all the fruits should be at least 5. // of all the fruits should be at least 5.
use std::collections::HashMap; use std::collections::HashMap;

View File

@ -1,8 +1,8 @@
// This function returns how much ice cream there is left in the fridge. // This function returns how much icecream there is left in the fridge.
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, // If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
// someone eats it all, so no ice cream is left (value 0). Return `None` if // someone eats it all, so no icecream is left (value 0). Return `None` if
// `hour_of_day` is higher than 23. // `hour_of_day` is higher than 23.
fn maybe_ice_cream(hour_of_day: u16) -> Option<u16> { fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
match hour_of_day { match hour_of_day {
0..=21 => Some(5), 0..=21 => Some(5),
22..=23 => Some(0), 22..=23 => Some(0),
@ -21,19 +21,19 @@ mod tests {
#[test] #[test]
fn raw_value() { fn raw_value() {
// Using `unwrap` is fine in a test. // Using `unwrap` is fine in a test.
let ice_creams = maybe_ice_cream(12).unwrap(); let icecreams = maybe_icecream(12).unwrap();
assert_eq!(ice_creams, 5); assert_eq!(icecreams, 5);
} }
#[test] #[test]
fn check_ice_cream() { fn check_icecream() {
assert_eq!(maybe_ice_cream(0), Some(5)); assert_eq!(maybe_icecream(0), Some(5));
assert_eq!(maybe_ice_cream(9), Some(5)); assert_eq!(maybe_icecream(9), Some(5));
assert_eq!(maybe_ice_cream(18), Some(5)); assert_eq!(maybe_icecream(18), Some(5));
assert_eq!(maybe_ice_cream(22), Some(0)); assert_eq!(maybe_icecream(22), Some(0));
assert_eq!(maybe_ice_cream(23), Some(0)); assert_eq!(maybe_icecream(23), Some(0));
assert_eq!(maybe_ice_cream(24), None); assert_eq!(maybe_icecream(24), None);
assert_eq!(maybe_ice_cream(25), None); assert_eq!(maybe_icecream(25), None);
} }
} }

View File

@ -6,7 +6,7 @@
// //
// In short, this particular use case for boxes is for when you want to own a // In short, this particular use case for boxes is for when you want to own a
// value and you care only that it is a type which implements a particular // value and you care only that it is a type which implements a particular
// trait. To do so, the `Box` is declared as of type `Box<dyn Trait>` where // trait. To do so, The `Box` is declared as of type `Box<dyn Trait>` where
// `Trait` is the trait the compiler looks for on any value used in that // `Trait` is the trait the compiler looks for on any value used in that
// context. For this exercise, that context is the potential errors which // context. For this exercise, that context is the potential errors which
// can be returned in a `Result`. // can be returned in a `Result`.

View File

@ -4,7 +4,7 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
fn main() { fn main() {
let string1 = String::from("long string is long"); let string1 = String::from("long string is long");
// Solution 1: You can move `strings2` out of the inner block so that it is // Solution1: You can move `strings2` out of the inner block so that it is
// not dropped before the print statement. // not dropped before the print statement.
let string2 = String::from("xyz"); let string2 = String::from("xyz");
let result; let result;
@ -21,7 +21,7 @@ fn main() {
{ {
let string2 = String::from("xyz"); let string2 = String::from("xyz");
result = longest(&string1, &string2); result = longest(&string1, &string2);
// Solution 2: You can move the print statement into the inner block so // Solution2: You can move the print statement into the inner block so
// that it is executed before `string2` is dropped. // that it is executed before `string2` is dropped.
println!("The longest string is '{result}'"); println!("The longest string is '{result}'");
// `string2` dropped here (end of the inner scope). // `string2` dropped here (end of the inner scope).

View File

@ -1,4 +1,4 @@
// Added the `macro_use` attribute. // Added the attribute `macro_use` attribute.
#[macro_use] #[macro_use]
mod macros { mod macros {
macro_rules! my_macro { macro_rules! my_macro {

View File

@ -1,5 +1,6 @@
use std::mem; use std::mem;
#[rustfmt::skip]
#[allow(unused_variables, unused_assignments)] #[allow(unused_variables, unused_assignments)]
fn main() { fn main() {
let my_option: Option<&str> = None; let my_option: Option<&str> = None;
@ -10,18 +11,17 @@ fn main() {
} }
// A comma was missing. // A comma was missing.
#[rustfmt::skip]
let my_arr = &[ let my_arr = &[
-1, -2, -3, -1, -2, -3,
-4, -5, -6, -4, -5, -6,
]; ];
println!("My array! Here it is: {my_arr:?}"); println!("My array! Here it is: {my_arr:?}");
let mut my_vec = vec![1, 2, 3, 4, 5]; let mut my_empty_vec = vec![1, 2, 3, 4, 5];
// `resize` mutates a vector instead of returning a new one. // `resize` mutates a vector instead of returning a new one.
// `resize(0, …)` clears a vector, so it is better to use `clear`. // `resize(0, …)` clears a vector, so it is better to use `clear`.
my_vec.clear(); my_empty_vec.clear();
println!("This Vec is empty, see? {my_vec:?}"); println!("This Vec is empty, see? {my_empty_vec:?}");
let mut value_a = 45; let mut value_a = 45;
let mut value_b = 66; let mut value_b = 66;

View File

@ -74,7 +74,7 @@ pub fn init() -> Result<()> {
let workspace_manifest_content = fs::read_to_string(&workspace_manifest) let workspace_manifest_content = fs::read_to_string(&workspace_manifest)
.with_context(|| format!("Failed to read the file {}", workspace_manifest.display()))?; .with_context(|| format!("Failed to read the file {}", workspace_manifest.display()))?;
if !workspace_manifest_content.contains("[workspace]") if !workspace_manifest_content.contains("[workspace]\n")
&& !workspace_manifest_content.contains("workspace.") && !workspace_manifest_content.contains("workspace.")
{ {
bail!( bail!(