feat: completed solutions

This commit is contained in:
2026-03-23 03:36:33 -04:00
parent 2279bea6f1
commit f568c094cb
65 changed files with 424 additions and 139 deletions
+65 -1
View File
@@ -1,6 +1,6 @@
DON'T EDIT THIS FILE!
structs1
as_ref_mut
intro1
intro2
@@ -32,3 +32,67 @@ move_semantics2
move_semantics3
move_semantics4
move_semantics5
structs1
structs2
structs3
enums1
enums2
enums3
strings1
strings2
strings3
strings4
modules1
modules2
modules3
hashmaps1
hashmaps2
hashmaps3
quiz2
options1
options2
options3
errors1
errors2
errors3
errors4
errors5
errors6
generics1
generics2
traits1
traits2
traits3
traits4
traits5
quiz3
lifetimes1
lifetimes2
lifetimes3
tests1
tests2
tests3
iterators1
iterators2
iterators3
iterators4
iterators5
box1
rc1
arc1
cow1
threads1
threads2
threads3
macros1
macros2
macros3
macros4
clippy1
clippy2
clippy3
using_as
from_into
from_str
try_from_into
as_ref_mut
+12 -5
View File
@@ -1,12 +1,15 @@
struct ColorRegularStruct {
// TODO: Add the fields that the test `regular_structs` expects.
// What types should the fields have? What are the minimum and maximum values for RGB colors?
red: u8,
green: u8,
blue: u8,
}
struct ColorTupleStruct(/* TODO: Add the fields that the test `tuple_structs` expects */);
struct ColorTupleStruct(u8, u8, u8);
#[derive(Debug)]
struct UnitStruct;
struct UnitStruct();
fn main() {
// You can optionally experiment here.
@@ -19,7 +22,11 @@ mod tests {
#[test]
fn regular_structs() {
// TODO: Instantiate a regular struct.
// let green =
let green = ColorRegularStruct {
red: 0,
green: 255,
blue: 0,
};
assert_eq!(green.red, 0);
assert_eq!(green.green, 255);
@@ -29,7 +36,7 @@ mod tests {
#[test]
fn tuple_structs() {
// TODO: Instantiate a tuple struct.
// let green =
let green = ColorTupleStruct(0, 255, 0);
assert_eq!(green.0, 0);
assert_eq!(green.1, 255);
@@ -39,7 +46,7 @@ mod tests {
#[test]
fn unit_structs() {
// TODO: Instantiate a unit struct.
// let unit_struct =
let unit_struct = UnitStruct();
let message = format!("{unit_struct:?}s are fun!");
assert_eq!(message, "UnitStructs are fun!");
+5 -1
View File
@@ -34,7 +34,11 @@ mod tests {
let order_template = create_order_template();
// TODO: Create your own order using the update syntax and template above!
// let your_order =
let your_order = Order {
name: "Hacker in Rust".to_string(),
count: 1,
..order_template
};
assert_eq!(your_order.name, "Hacker in Rust");
assert_eq!(your_order.year, order_template.year);
+4 -2
View File
@@ -24,14 +24,16 @@ impl Package {
}
// TODO: Add the correct return type to the function signature.
fn is_international(&self) {
fn is_international(&self) -> bool {
// TODO: Read the tests that use this method to find out when a package
// is considered international.
self.sender_country != self.recipient_country
}
// TODO: Add the correct return type to the function signature.
fn get_fees(&self, cents_per_gram: u32) {
fn get_fees(&self, cents_per_gram: u32) -> u32 {
// TODO: Calculate the package's fees.
cents_per_gram * self.weight_in_grams
}
}
+5
View File
@@ -1,6 +1,11 @@
#[derive(Debug)]
enum Message {
// TODO: Define a few types of messages as used below.
Resize,
Move,
Echo,
ChangeColor,
Quit,
}
fn main() {
+5
View File
@@ -7,6 +7,11 @@ struct Point {
#[derive(Debug)]
enum Message {
// TODO: Define the different variants used below.
Resize { width: usize, height: usize },
Move(Point),
Echo(String),
ChangeColor(u8, u8, u8),
Quit,
}
impl Message {
+7
View File
@@ -46,6 +46,13 @@ impl State {
fn process(&mut self, message: Message) {
// TODO: Create a match expression to process the different message
// variants using the methods defined above.
match message {
Message::Resize { width, height } => self.resize(width, height),
Message::Move(point) => self.move_position(point),
Message::Echo(string) => self.echo(string),
Message::ChangeColor(red, green, blue) => self.change_color(red, green, blue),
Message::Quit => self.quit(),
}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
// TODO: Fix the compiler error without changing the function signature.
fn current_favorite_color() -> String {
fn current_favorite_color() -> &'static str {
"blue"
}
+1 -1
View File
@@ -6,7 +6,7 @@ fn is_a_color_word(attempt: &str) -> bool {
fn main() {
let word = String::from("green"); // Don't change this line.
if is_a_color_word(word) {
if is_a_color_word(&word) {
println!("That is a color word I know!");
} else {
println!("That is not a color word I know.");
+3
View File
@@ -1,13 +1,16 @@
fn trim_me(input: &str) -> &str {
// TODO: Remove whitespace from both ends of a string.
input.trim_ascii()
}
fn compose_me(input: &str) -> String {
// TODO: Add " world!" to the string! There are multiple ways to do this.
input.to_owned() + " world!"
}
fn replace_me(input: &str) -> String {
// TODO: Replace "cars" in the string with "balloons".
input.to_owned().replace("cars", "balloons")
}
fn main() {
+10 -13
View File
@@ -1,6 +1,3 @@
// Calls of this function should be replaced with calls of `string_slice` or `string`.
fn placeholder() {}
fn string_slice(arg: &str) {
println!("{arg}");
}
@@ -13,25 +10,25 @@ fn string(arg: String) {
// Your task is to replace `placeholder(…)` with either `string_slice(…)`
// or `string(…)` depending on what you think each value is.
fn main() {
placeholder("blue");
string_slice("blue"); // &str
placeholder("red".to_string());
string("red".to_string()); // String
placeholder(String::from("hi"));
string(String::from("hi")); // String
placeholder("rust is fun!".to_owned());
string("rust is fun!".to_owned()); // String
placeholder("nice weather".into());
string("nice weather".into()); // String
placeholder(format!("Interpolation {}", "Station"));
string(format!("Interpolation {}", "Station")); // String
// WARNING: This is byte indexing, not character indexing.
// Character indexing can be done using `s.chars().nth(INDEX)`.
placeholder(&String::from("abc")[0..1]);
string_slice(&String::from("abc")[0..1]); // &str
placeholder(" hello there ".trim());
string_slice(" hello there ".trim()); // &str
placeholder("Happy Monday!".replace("Mon", "Tues"));
string("Happy Monday!".replace("Mon", "Tues")); // String
placeholder("mY sHiFt KeY iS sTiCkY".to_lowercase());
string("mY sHiFt KeY iS sTiCkY".to_lowercase()); // String
}
+1 -1
View File
@@ -5,7 +5,7 @@ mod sausage_factory {
String::from("Ginger")
}
fn make_sausage() {
pub fn make_sausage() {
get_secret_recipe();
println!("sausage!");
}
+2 -2
View File
@@ -3,8 +3,8 @@
mod delicious_snacks {
// TODO: Add the following two `use` statements after fixing them.
// use self::fruits::PEAR as ???;
// use self::veggies::CUCUMBER as ???;
pub use self::fruits::PEAR as fruit;
pub use self::veggies::CUCUMBER as veggie;
mod fruits {
pub const PEAR: &str = "Pear";
+1 -1
View File
@@ -3,7 +3,7 @@
// TODO: Bring `SystemTime` and `UNIX_EPOCH` from the `std::time` module into
// your scope. Bonus style points if you can do it with one line!
// use ???;
use std::time::{SystemTime, UNIX_EPOCH};
fn main() {
match SystemTime::now().duration_since(UNIX_EPOCH) {
+3 -1
View File
@@ -8,10 +8,12 @@ use std::collections::HashMap;
fn fruit_basket() -> HashMap<String, u32> {
// TODO: Declare the hash map.
// let mut basket =
let mut basket = HashMap::<String, u32>::new();
// Two bananas are already given for you :)
basket.insert(String::from("banana"), 2);
basket.insert(String::from("apple"), 2);
basket.insert(String::from("mango"), 2);
// TODO: Put more fruits in your basket.
+1
View File
@@ -32,6 +32,7 @@ fn fruit_basket(basket: &mut HashMap<Fruit, u32>) {
// TODO: Insert new fruits if they are not already present in the
// basket. Note that you are not allowed to put any type of fruit that's
// already present!
basket.entry(fruit).or_insert(1);
}
}
+39 -3
View File
@@ -31,13 +31,47 @@ fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
// Keep in mind that goals scored by team 1 will be the number of goals
// conceded by team 2. Similarly, goals scored by team 2 will be the
// number of goals conceded by team 1.
}
scores
.entry(team_1_name)
.and_modify(|current_team_1_scores| {
current_team_1_scores.goals_scored += team_1_score;
current_team_1_scores.goals_conceded += team_2_score
})
.or_insert(TeamScores {
goals_scored: team_1_score,
goals_conceded: team_2_score,
});
scores
.entry(team_2_name)
.and_modify(|current_team_2_scores| {
current_team_2_scores.goals_scored += team_2_score;
current_team_2_scores.goals_conceded += team_1_score
})
.or_insert(TeamScores {
goals_scored: team_2_score,
goals_conceded: team_1_score,
});
}
scores
}
fn main() {
// You can optionally experiment here.
const RESULTS: &str = "England,France,4,2
France,Italy,3,1
Poland,Spain,2,0
Germany,England,2,1
England,Spain,1,0";
let scores = build_scores_table(RESULTS);
for (key, value) in &scores {
println!(
"{key}: (scored: {0}, conceeded: {1})",
value.goals_scored, value.goals_conceded
);
}
}
#[cfg(test)]
@@ -54,9 +88,11 @@ England,Spain,1,0";
fn build_scores() {
let scores = build_scores_table(RESULTS);
assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"]
assert!(
["England", "France", "Germany", "Italy", "Poland", "Spain"]
.into_iter()
.all(|team_name| scores.contains_key(team_name)));
.all(|team_name| scores.contains_key(team_name))
);
}
#[test]
+6 -1
View File
@@ -4,6 +4,11 @@
// `hour_of_day` is higher than 23.
fn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {
// TODO: Complete the function body.
match hour_of_day {
0..22 => Some(5),
22..=23 => Some(0),
_ => None,
}
}
fn main() {
@@ -18,7 +23,7 @@ mod tests {
fn raw_value() {
// TODO: Fix this test. How do you get the value contained in the
// Option?
let ice_creams = maybe_ice_cream(12);
let ice_creams = maybe_ice_cream(12).unwrap();
assert_eq!(ice_creams, 5); // Don't change this line.
}
+2 -2
View File
@@ -10,7 +10,7 @@ mod tests {
let optional_target = Some(target);
// TODO: Make this an if-let statement whose value is `Some`.
word = optional_target {
if let Some(word) = optional_target {
assert_eq!(word, target);
}
}
@@ -29,7 +29,7 @@ mod tests {
// TODO: Make this a while-let statement. Remember that `Vec::pop()`
// adds another layer of `Option`. You can do nested pattern matching
// in if-let and while-let statements.
integer = optional_integers.pop() {
while let Some(Some(integer)) = optional_integers.pop() {
assert_eq!(integer, cursor);
cursor -= 1;
}
+1 -1
View File
@@ -9,7 +9,7 @@ fn main() {
// TODO: Fix the compiler error by adding something to this match statement.
match optional_point {
Some(p) => println!("Coordinates are {},{}", p.x, p.y),
Some(ref p) => println!("Coordinates are {},{}", p.x, p.y),
_ => panic!("No match!"),
}
+3 -3
View File
@@ -4,12 +4,12 @@
// construct to `Option` that can be used to express error conditions. Change
// the function signature and body to return `Result<String, String>` instead
// of `Option<String>`.
fn generate_nametag_text(name: String) -> Option<String> {
fn generate_nametag_text(name: String) -> Result<String, String> {
if name.is_empty() {
// Empty names aren't allowed
None
Err("Empty names aren't allowed".into())
} else {
Some(format!("Hi! My name is {name}"))
Ok(format!("Hi! My name is {name}"))
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let cost_per_item = 5;
// TODO: Handle the error case as described above.
let qty = item_quantity.parse::<i32>();
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
+3 -1
View File
@@ -15,7 +15,7 @@ fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
// TODO: Fix the compiler error by changing the signature and body of the
// `main` function.
fn main() {
fn main() -> Result<(), ParseIntError> {
let mut tokens = 100;
let pretend_user_input = "8";
@@ -24,8 +24,10 @@ fn main() {
if cost > tokens {
println!("You can't afford that many!");
Ok(())
} else {
tokens -= cost;
println!("You now have {tokens} tokens.");
Ok(())
}
}
+5 -1
View File
@@ -11,7 +11,11 @@ impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> {
// TODO: This function shouldn't always return an `Ok`.
// Read the tests below to clarify what should be returned.
Ok(Self(value as u64))
match value {
x if x < 0 => Err(CreationError::Negative),
0 => Err(CreationError::Zero),
x => Ok(PositiveNonzeroInteger(x as u64)),
}
}
}
+1 -1
View File
@@ -48,7 +48,7 @@ impl PositiveNonzeroInteger {
// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we
// use to describe both errors? Is there a trait which both errors implement?
fn main() {
fn main() -> Result<(), Box<dyn Error>> {
let pretend_user_input = "42";
let x: i64 = pretend_user_input.parse()?;
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
+4 -2
View File
@@ -25,7 +25,9 @@ impl ParsePosNonzeroError {
}
// TODO: Add another error conversion function here.
// fn from_parse_int(???) -> Self { ??? }
fn from_parse_int(err: ParseIntError) -> Self {
Self::ParseInt(err)
}
}
#[derive(PartialEq, Debug)]
@@ -43,7 +45,7 @@ impl PositiveNonzeroInteger {
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
// TODO: change this to return an appropriate error instead of panicking
// when `parse()` returns an error.
let x: i64 = s.parse().unwrap();
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ fn main() {
// TODO: Fix the compiler error by annotating the type of the vector
// `Vec<T>`. Choose `T` as some integer type that can be created from
// `u8` and `i8`.
let mut numbers = Vec::new();
let mut numbers: Vec<i32> = Vec::new();
// Don't change the lines below.
let n1: u8 = 42;
+4 -4
View File
@@ -1,12 +1,12 @@
// This powerful wrapper provides the ability to store a positive integer value.
// TODO: Rewrite it using a generic so that it supports wrapping ANY type.
struct Wrapper {
value: u32,
struct Wrapper<T> {
value: T,
}
// TODO: Adapt the struct's implementation to be generic over the wrapped value.
impl Wrapper {
fn new(value: u32) -> Self {
impl<T> Wrapper<T> {
fn new(value: T) -> Self {
Wrapper { value }
}
}
+3
View File
@@ -6,6 +6,9 @@ trait AppendBar {
impl AppendBar for String {
// TODO: Implement `AppendBar` for the type `String`.
fn append_bar(self) -> String {
self + "Bar"
}
}
fn main() {
+6
View File
@@ -4,6 +4,12 @@ trait AppendBar {
// TODO: Implement the trait `AppendBar` for a vector of strings.
// `append_bar` should push the string "Bar" into the vector.
impl AppendBar for Vec<String> {
fn append_bar(mut self) -> Self {
self.push("Bar".into());
self
}
}
fn main() {
// You can optionally experiment here.
+6 -1
View File
@@ -3,7 +3,9 @@ trait Licensed {
// implementors like the two structs below can share that default behavior
// without repeating the function.
// The default license information should be the string "Default license".
fn licensing_info(&self) -> String;
fn licensing_info(&self) -> String {
"Default license".into()
}
}
struct SomeSoftware {
@@ -28,10 +30,13 @@ mod tests {
#[test]
fn is_licensing_info_the_same() {
let licensing_info = "Default license";
let some_software = SomeSoftware { version_number: 1 };
let other_software = OtherSoftware {
version_number: "v2.0.0".to_string(),
};
assert_eq!(some_software.licensing_info(), licensing_info);
assert_eq!(other_software.licensing_info(), licensing_info);
}
+1 -1
View File
@@ -11,7 +11,7 @@ impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}
// TODO: Fix the compiler error by only changing the signature of this function.
fn compare_license_types(software1: ???, software2: ???) -> bool {
fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {
software1.licensing_info() == software2.licensing_info()
}
+1 -1
View File
@@ -19,7 +19,7 @@ impl SomeTrait for OtherStruct {}
impl OtherTrait for OtherStruct {}
// TODO: Fix the compiler error by only changing the signature of this function.
fn some_func(item: ???) -> bool {
fn some_func(item: impl SomeTrait + OtherTrait) -> bool {
item.some_function() && item.other_function()
}
+2 -6
View File
@@ -4,12 +4,8 @@
// not own their own data. What if their owner goes out of scope?
// TODO: Fix the compiler error by updating the function signature.
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
+2 -6
View File
@@ -1,19 +1,15 @@
// Don't change this function.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
if x.len() > y.len() { x } else { y }
}
fn main() {
// TODO: Fix the compiler error by moving one line.
let string1 = String::from("long string is long");
let string2 = String::from("xyz");
let result;
{
let string2 = String::from("xyz");
result = longest(&string1, &string2);
}
println!("The longest string is '{result}'");
+3 -3
View File
@@ -1,9 +1,9 @@
// Lifetimes are also needed when structs hold references.
// TODO: Fix the compiler errors about the struct.
struct Book {
author: &str,
title: &str,
struct Book<'a> {
author: &'a str,
title: &'a str,
}
fn main() {
+3 -2
View File
@@ -13,11 +13,12 @@ fn main() {
mod tests {
// TODO: Import `is_even`. You can use a wildcard to import everything in
// the outer module.
use super::is_even;
#[test]
fn you_can_assert() {
// TODO: Test the function `is_even` with some values.
assert!();
assert!();
assert!(is_even(12));
assert!(!is_even(13));
}
}
+4 -4
View File
@@ -15,9 +15,9 @@ mod tests {
#[test]
fn you_can_assert_eq() {
// TODO: Test the function `power_of_2` with some values.
assert_eq!();
assert_eq!();
assert_eq!();
assert_eq!();
assert_eq!(power_of_2(0), 1);
assert_eq!(power_of_2(1), 2);
assert_eq!(power_of_2(2), 4);
assert_eq!(power_of_2(3), 8);
}
}
+4 -2
View File
@@ -29,13 +29,14 @@ mod tests {
// TODO: This test should check if the rectangle has the size that we
// pass to its constructor.
let rect = Rectangle::new(10, 20);
assert_eq!(todo!(), 10); // Check width
assert_eq!(todo!(), 20); // Check height
assert_eq!(rect.width, 10); // Check width
assert_eq!(rect.height, 20); // Check height
}
// TODO: This test should check if the program panics when we try to create
// a rectangle with negative width.
#[test]
#[should_panic]
fn negative_width() {
let _rect = Rectangle::new(-10, 10);
}
@@ -43,6 +44,7 @@ mod tests {
// TODO: This test should check if the program panics when we try to create
// a rectangle with negative height.
#[test]
#[should_panic]
fn negative_height() {
let _rect = Rectangle::new(10, -10);
}
+4 -4
View File
@@ -13,13 +13,13 @@ mod tests {
let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"];
// TODO: Create an iterator over the array.
let mut fav_fruits_iterator = todo!();
let mut fav_fruits_iterator = my_fav_fruits.iter();
assert_eq!(fav_fruits_iterator.next(), Some(&"banana"));
assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()`
assert_eq!(fav_fruits_iterator.next(), Some(&"custard apple")); // TODO: Replace `todo!()`
assert_eq!(fav_fruits_iterator.next(), Some(&"avocado"));
assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()`
assert_eq!(fav_fruits_iterator.next(), Some(&"peach")); // TODO: Replace `todo!()`
assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry"));
assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()`
assert_eq!(fav_fruits_iterator.next(), None); // TODO: Replace `todo!()`
}
}
+4 -3
View File
@@ -5,9 +5,10 @@
// "hello" -> "Hello"
fn capitalize_first(input: &str) -> String {
let mut chars = input.chars();
match chars.next() {
None => String::new(),
Some(first) => todo!(),
Some(first) => first.to_uppercase().chain(chars).collect(),
}
}
@@ -15,14 +16,14 @@ fn capitalize_first(input: &str) -> String {
// Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"]
fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
// ???
words.iter().map(|word| capitalize_first(word)).collect()
}
// TODO: Apply the `capitalize_first` function again to a slice of string
// slices. Return a single string.
// ["hello", " ", "world"] -> "Hello World"
fn capitalize_words_string(words: &[&str]) -> String {
// ???
words.iter().map(|word| capitalize_first(word)).collect()
}
fn main() {
+20 -5
View File
@@ -11,21 +11,36 @@ enum DivisionError {
// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
// Otherwise, return a suitable error.
fn divide(a: i64, b: i64) -> Result<i64, DivisionError> {
todo!();
match (a, b) {
(_, 0) => Err(DivisionError::DivideByZero),
(a, b) if a == i64::MIN && b == -1 => Err(DivisionError::IntegerOverflow),
(a, b) if a % b != 0 => Err(DivisionError::NotDivisible),
(a, b) => Ok(a / b),
}
}
// TODO: Add the correct return type and complete the function body.
// Desired output: `Ok([1, 11, 1426, 3])`
fn result_with_list() {
fn result_with_list() -> Result<Vec<i64>, DivisionError> {
let numbers = [27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
let result: Vec<i64> = numbers
.into_iter()
.map(|n| divide(n, 27))
.collect::<Result<Vec<i64>, DivisionError>>()?;
Ok(result)
}
// TODO: Add the correct return type and complete the function body.
// Desired output: `[Ok(1), Ok(11), Ok(1426), Ok(3)]`
fn list_of_results() {
fn list_of_results() -> Vec<Result<i64, DivisionError>> {
let numbers = [27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
let division_results: Vec<Result<i64, DivisionError>> = numbers
.into_iter()
.map(|n| divide(n, 27))
.collect::<Vec<Result<i64, DivisionError>>>();
division_results
}
fn main() {
+1
View File
@@ -10,6 +10,7 @@ fn factorial(num: u64) -> u64 {
// - additional variables
// For an extra challenge, don't use:
// - recursion
(1..=num).fold(1, |acc, e| acc * e)
}
fn main() {
+8
View File
@@ -28,6 +28,9 @@ fn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {
fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
// `map` is a hash map with `String` keys and `Progress` values.
// map = { "variables1": Complete, "from_str": None, … }
map.values()
.filter(|excercise| **excercise == value)
.count()
}
fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
@@ -48,6 +51,11 @@ fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Pr
// `collection` is a slice of hash maps.
// collection = [{ "variables1": Complete, "from_str": None, … },
// { "variables2": Complete, … }, … ]
collection
.iter()
.flatten()
.filter(|excercise| *excercise.1 == value)
.count()
}
fn main() {
+2 -2
View File
@@ -23,13 +23,13 @@ fn main() {
let numbers: Vec<_> = (0..100u32).collect();
// TODO: Define `shared_numbers` by using `Arc`.
// let shared_numbers = ???;
let shared_numbers = Arc::new(numbers);
let mut join_handles = Vec::new();
for offset in 0..8 {
// TODO: Define `child_numbers` using `shared_numbers`.
// let child_numbers = ???;
let child_numbers = Arc::clone(&shared_numbers);
let handle = thread::spawn(move || {
let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();
+3 -3
View File
@@ -12,18 +12,18 @@
// TODO: Use a `Box` in the enum definition to make the code compile.
#[derive(PartialEq, Debug)]
enum List {
Cons(i32, List),
Cons(i32, Box<List>),
Nil,
}
// TODO: Create an empty cons list.
fn create_empty_list() -> List {
todo!()
List::Nil
}
// TODO: Create a non-empty cons list.
fn create_non_empty_list() -> List {
todo!()
List::Cons(64, Box::new(List::Nil))
}
fn main() {
+3 -3
View File
@@ -39,7 +39,7 @@ mod tests {
let mut input = Cow::from(&vec);
abs_all(&mut input);
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
assert!(matches!(input, todo!()));
assert!(matches!(input, Cow::Borrowed(_)));
}
#[test]
@@ -52,7 +52,7 @@ mod tests {
let mut input = Cow::from(vec);
abs_all(&mut input);
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
assert!(matches!(input, todo!()));
assert!(matches!(input, Cow::Owned(_)));
}
#[test]
@@ -64,6 +64,6 @@ mod tests {
let mut input = Cow::from(vec);
abs_all(&mut input);
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
assert!(matches!(input, todo!()));
assert!(matches!(input, Cow::Owned(_)));
}
}
+6 -6
View File
@@ -60,17 +60,17 @@ mod tests {
jupiter.details();
// TODO
let saturn = Planet::Saturn(Rc::new(Sun));
let saturn = Planet::Saturn(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
saturn.details();
// TODO
let uranus = Planet::Uranus(Rc::new(Sun));
let uranus = Planet::Uranus(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
uranus.details();
// TODO
let neptune = Planet::Neptune(Rc::new(Sun));
let neptune = Planet::Neptune(Rc::clone(&sun));
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
neptune.details();
@@ -91,13 +91,13 @@ mod tests {
drop(mars);
println!("reference count = {}", Rc::strong_count(&sun)); // 4 references
// TODO
drop(earth);
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
// TODO
drop(venus);
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
// TODO
drop(mercury);
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
assert_eq!(Rc::strong_count(&sun), 1);
+3 -1
View File
@@ -20,10 +20,12 @@ fn main() {
handles.push(handle);
}
let mut results = Vec::new();
let mut results: Vec<_> = Vec::<_>::new();
for handle in handles {
// TODO: Collect the results of all threads into the `results` vector.
// Use the `JoinHandle` struct which is returned by `thread::spawn`.
results.push(handle.join().unwrap())
}
if results.len() != 10 {
+13 -4
View File
@@ -2,24 +2,31 @@
// work. But this time, the spawned threads need to be in charge of updating a
// shared value: `JobStatus.jobs_done`
use std::{sync::Arc, thread, time::Duration};
use std::{
sync::{Arc, Mutex},
thread,
time::Duration,
};
#[derive(Debug)]
struct JobStatus {
jobs_done: u32,
}
fn main() {
// TODO: `Arc` isn't enough if you want a **mutable** shared state.
let status = Arc::new(JobStatus { jobs_done: 0 });
let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 }));
let mut handles = Vec::new();
for _ in 0..10 {
let status_shared = Arc::clone(&status);
let handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(250));
// TODO: You must take an action before you update a shared value.
status_shared.jobs_done += 1;
let mut acquired_status = status_shared.lock().unwrap();
acquired_status.jobs_done += 1;
});
handles.push(handle);
}
@@ -30,5 +37,7 @@ fn main() {
}
// TODO: Print the value of `JobStatus.jobs_done`.
println!("Jobs done: {}", todo!());
let mutex = Arc::try_unwrap(status).unwrap();
let job_status = mutex.into_inner().unwrap();
println!("Jobs done: {}", job_status.jobs_done);
}
+7 -2
View File
@@ -17,18 +17,23 @@ impl Queue {
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) {
// TODO: We want to send `tx` to both threads. But currently, it is moved
// into the first thread. How could you solve this problem?
let transmission = tx.clone();
thread::spawn(move || {
for val in q.first_half {
println!("Sending {val:?}");
tx.send(val).unwrap();
transmission.send(val).unwrap();
thread::sleep(Duration::from_millis(250));
}
});
let transmission = tx.clone();
thread::spawn(move || {
for val in q.second_half {
println!("Sending {val:?}");
tx.send(val).unwrap();
transmission.send(val).unwrap();
thread::sleep(Duration::from_millis(250));
}
});
+1 -1
View File
@@ -6,5 +6,5 @@ macro_rules! my_macro {
fn main() {
// TODO: Fix the macro call.
my_macro();
my_macro!();
}
+4 -4
View File
@@ -1,10 +1,10 @@
fn main() {
my_macro!();
}
// TODO: Fix the compiler error by moving the whole definition of this macro.
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
fn main() {
my_macro!();
}
+1
View File
@@ -1,6 +1,7 @@
// TODO: Fix the compiler error without taking the macro definition out of this
// module.
mod macros {
#[macro_export]
macro_rules! my_macro {
() => {
println!("Check out my macro!");
+1 -1
View File
@@ -3,7 +3,7 @@
macro_rules! my_macro {
() => {
println!("Check out my macro!");
}
};
($val:expr) => {
println!("Look at this other macro: {}", $val);
}
+1 -1
View File
@@ -6,7 +6,7 @@
fn main() {
// TODO: Fix the Clippy lint in this line.
let pi = 3.14;
let pi = std::f32::consts::PI;
let radius: f32 = 5.0;
let area = pi * radius.powi(2);
+2 -1
View File
@@ -1,8 +1,9 @@
fn main() {
let mut res = 42;
let option = Some(12);
// TODO: Fix the Clippy lint.
for x in option {
if let Some(x) = option {
res += x;
}
+6 -6
View File
@@ -7,23 +7,23 @@ fn main() {
let my_option: Option<&str> = None;
// Assume that you don't know the value of `my_option`.
// In the case of `Some`, we want to print its value.
if my_option.is_none() {
println!("{}", my_option.unwrap());
if let Some(my_option) = my_option {
println!("{}", my_option);
}
let my_arr = &[
-1, -2, -3
-1, -2, -3,
-4, -5, -6
];
println!("My array! Here it is: {my_arr:?}");
let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
let my_empty_vec = ();
vec![1, 2, 3, 4, 5].resize(0, 5);
println!("This Vec is empty, see? {my_empty_vec:?}");
let mut value_a = 45;
let mut value_b = 66;
// Let's swap these two!
value_a = value_b;
value_b = value_a;
std::mem::swap(&mut value_a, &mut value_b);
println!("value a: {value_a}; value b: {value_b}");
}
+5 -3
View File
@@ -5,20 +5,22 @@
// Obtain the number of bytes (not characters) in the given argument
// (`.len()` returns the number of bytes in a string).
// TODO: Add the `AsRef` trait appropriately as a trait bound.
fn byte_counter<T>(arg: T) -> usize {
fn byte_counter<T: AsRef<str>>(arg: T) -> usize {
arg.as_ref().len()
}
// Obtain the number of characters (not bytes) in the given argument.
// TODO: Add the `AsRef` trait appropriately as a trait bound.
fn char_counter<T>(arg: T) -> usize {
fn char_counter<T: AsRef<str>>(arg: T) -> usize {
arg.as_ref().chars().count()
}
// Squares a number using `as_mut()`.
// TODO: Add the appropriate trait bound.
fn num_sq<T>(arg: &mut T) {
fn num_sq<T: AsMut<u32>>(arg: &mut T) {
// TODO: Implement the function body.
let val = *arg.as_mut();
*arg.as_mut() = val * val;
}
fn main() {
+12 -1
View File
@@ -34,7 +34,18 @@ impl Default for Person {
// 5. Parse the second element from the split operation into a `u8` as the age.
// 6. If parsing the age fails, return the default of `Person`.
impl From<&str> for Person {
fn from(s: &str) -> Self {}
fn from(s: &str) -> Self {
match s.split(",").collect::<Vec<&str>>().as_slice() {
[name, age] if (!name.is_empty() && !age.is_empty()) => match age.parse::<u8>() {
Ok(age) => Person {
name: name.to_string(),
age,
},
Err(_) => Person::default(),
},
_ => Person::default(),
}
}
}
fn main() {
+13 -1
View File
@@ -41,7 +41,19 @@ enum ParsePersonError {
impl FromStr for Person {
type Err = ParsePersonError;
fn from_str(s: &str) -> Result<Self, Self::Err> {}
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split(",").collect::<Vec<&str>>().as_slice() {
[name, age] if (!name.is_empty()) => match age.parse::<u8>() {
Ok(age) => Ok(Person {
name: name.to_string(),
age,
}),
Err(err) => Err(ParsePersonError::ParseInt(err)),
},
["", _] => Err(ParsePersonError::NoName),
_ => Err(ParsePersonError::BadLen),
}
}
}
fn main() {
+50 -3
View File
@@ -28,14 +28,45 @@ enum IntoColorError {
impl TryFrom<(i16, i16, i16)> for Color {
type Error = IntoColorError;
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {}
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
match tuple {
(red, green, blue)
if (0..=255).contains(&red)
&& (0..=255).contains(&green)
&& (0..=255).contains(&blue) =>
{
Ok(Color {
red: red as u8,
green: green as u8,
blue: blue as u8,
})
}
_ => Err(IntoColorError::IntConversion),
}
}
}
// TODO: Array implementation.
impl TryFrom<[i16; 3]> for Color {
type Error = IntoColorError;
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {}
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
match arr.as_slice() {
[red, green, blue] => {
if (0..=255).contains(red) && (0..=255).contains(green) && (0..=255).contains(blue)
{
Ok(Color {
red: *red as u8,
green: *green as u8,
blue: *blue as u8,
})
} else {
Err(IntoColorError::IntConversion)
}
}
_ => Err(IntoColorError::BadLen),
}
}
}
// TODO: Slice implementation.
@@ -43,7 +74,23 @@ impl TryFrom<[i16; 3]> for Color {
impl TryFrom<&[i16]> for Color {
type Error = IntoColorError;
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {}
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
match slice {
[red, green, blue] => {
if (0..=255).contains(red) && (0..=255).contains(green) && (0..=255).contains(blue)
{
Ok(Color {
red: *red as u8,
green: *green as u8,
blue: *blue as u8,
})
} else {
Err(IntoColorError::IntConversion)
}
}
_ => Err(IntoColorError::BadLen),
}
}
}
fn main() {
+1 -1
View File
@@ -5,7 +5,7 @@
fn average(values: &[f64]) -> f64 {
let total = values.iter().sum::<f64>();
// TODO: Make a conversion before dividing.
total / values.len()
total / values.len() as f64
}
fn main() {
+16 -2
View File
@@ -27,7 +27,21 @@ mod my_module {
use super::Command;
// TODO: Complete the function as described above.
// pub fn transformer(input: ???) -> ??? { ??? }
pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> {
let mut output: Vec<String> = Vec::new();
for (string, command) in input {
match command {
Command::Uppercase => output.push(string.to_uppercase()),
Command::Trim => output.push(string.trim().into()),
Command::Append(number_of_times_bar_will_be_added) => {
output.push(string + &"bar".repeat(number_of_times_bar_will_be_added))
}
}
}
output
}
}
fn main() {
@@ -37,8 +51,8 @@ fn main() {
#[cfg(test)]
mod tests {
// TODO: What do we need to import to have `transformer` in scope?
// use ???;
use super::Command;
use super::my_module::transformer;
#[test]
fn it_works() {
+4 -3
View File
@@ -10,16 +10,17 @@
//
// Make the necessary code changes in the struct `ReportCard` and the impl
// block to support alphabetical report cards in addition to numerical ones.
use std::fmt::Display;
// TODO: Adjust the struct as described above.
struct ReportCard {
grade: f32,
struct ReportCard<G: Display> {
grade: G,
student_name: String,
student_age: u8,
}
// TODO: Adjust the impl block as described above.
impl ReportCard {
impl<G: Display> ReportCard<G> {
fn print(&self) -> String {
format!(
"{} ({}) - achieved a grade of {}",