record progress: tests, lifetimes, iterators

This commit is contained in:
Denis-Cosmin Nutiu 2024-11-03 12:39:49 +02:00
parent 3fcd3d8e03
commit 8e902435f4
20 changed files with 324 additions and 62 deletions

View file

@ -1,6 +1,6 @@
DON'T EDIT THIS FILE! DON'T EDIT THIS FILE!
lifetimes1 iterators2
intro1 intro1
intro2 intro2
@ -66,3 +66,10 @@ traits3
traits4 traits4
traits5 traits5
quiz3 quiz3
lifetimes1
lifetimes2
lifetimes3
tests1
tests2
tests3
iterators1

View file

@ -4,7 +4,7 @@
// not own their own data. What if their owner goes out of scope? // not own their own data. What if their owner goes out of scope?
// TODO: Fix the compiler error by updating the function signature. // TODO: Fix the compiler error by updating the function signature.
fn longest(x: &str, y: &str) -> &str { fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { if x.len() > y.len() {
x x
} else { } else {

View file

@ -15,6 +15,7 @@ fn main() {
{ {
let string2 = String::from("xyz"); let string2 = String::from("xyz");
result = longest(&string1, &string2); result = longest(&string1, &string2);
}
println!("The longest string is '{result}'"); println!("The longest string is '{result}'");
} }
}

View file

@ -1,9 +1,9 @@
// Lifetimes are also needed when structs hold references. // Lifetimes are also needed when structs hold references.
// TODO: Fix the compiler errors about the struct. // TODO: Fix the compiler errors about the struct.
struct Book { struct Book<'a > {
author: &str, author: &'a str,
title: &str, title: &'a str,
} }
fn main() { fn main() {

View file

@ -14,10 +14,12 @@ mod tests {
// TODO: Import `is_even`. You can use a wildcard to import everything in // TODO: Import `is_even`. You can use a wildcard to import everything in
// the outer module. // the outer module.
use crate::is_even;
#[test] #[test]
fn you_can_assert() { fn you_can_assert() {
// TODO: Test the function `is_even` with some values. // TODO: Test the function `is_even` with some values.
assert!(); assert!(is_even(4));
assert!(); assert!(is_even(6));
} }
} }

View file

@ -15,9 +15,9 @@ mod tests {
#[test] #[test]
fn you_can_assert_eq() { fn you_can_assert_eq() {
// TODO: Test the function `power_of_2` with some values. // TODO: Test the function `power_of_2` with some values.
assert_eq!(); assert_eq!(power_of_2(2), 4);
assert_eq!(); assert_eq!(power_of_2(2), 4);
assert_eq!(); assert_eq!(power_of_2(2), 4);
assert_eq!(); assert_eq!(power_of_2(2), 4);
} }
} }

View file

@ -26,23 +26,20 @@ mod tests {
#[test] #[test]
fn correct_width_and_height() { fn correct_width_and_height() {
// TODO: This test should check if the rectangle has the size that we
// pass to its constructor.
let rect = Rectangle::new(10, 20); let rect = Rectangle::new(10, 20);
assert_eq!(todo!(), 10); // Check width assert_eq!(rect.width, 10); // Check width
assert_eq!(todo!(), 20); // Check height 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] #[test]
#[should_panic]
fn negative_width() { fn negative_width() {
let _rect = Rectangle::new(-10, 10); let _rect = Rectangle::new(-10, 10);
} }
// TODO: This test should check if the program panics when we try to create
// a rectangle with negative height.
#[test] #[test]
#[should_panic]
fn negative_height() { fn negative_height() {
let _rect = Rectangle::new(10, -10); let _rect = Rectangle::new(10, -10);
} }

View file

@ -13,13 +13,13 @@ mod tests {
let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"];
// TODO: Create an iterator over the array. // 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(), Some(&"banana"));
assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` assert_eq!(fav_fruits_iterator.next(), Some(&"custard apple"));
assert_eq!(fav_fruits_iterator.next(), Some(&"avocado")); 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"));
assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); 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);
} }
} }

View file

@ -1,28 +1,35 @@
// In this exercise, you'll learn some of the unique advantages that iterators // In this exercise, you'll learn some of the unique advantages that iterators
// can offer. // can offer.
// TODO: Complete the `capitalize_first` function. // Complete the `capitalize_first` function.
// "hello" -> "Hello" // "hello" -> "Hello"
fn capitalize_first(input: &str) -> String { fn capitalize_first(input: &str) -> String {
let mut chars = input.chars(); let mut chars = input.chars();
match chars.next() { match chars.next() {
None => String::new(), None => String::new(),
Some(first) => todo!(), Some(first) => {
let mut new = String::from(first.to_uppercase().to_string());
new.push_str(&input[1..]);
new
},
} }
} }
// TODO: Apply the `capitalize_first` function to a slice of string slices.
// Return a vector of strings. // Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"] // ["hello", "world"] -> ["Hello", "World"]
fn capitalize_words_vector(words: &[&str]) -> Vec<String> { fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
// ??? words.iter().map(|i| {
capitalize_first(i)
}).collect()
} }
// TODO: Apply the `capitalize_first` function again to a slice of string
// slices. Return a single string. // slices. Return a single string.
// ["hello", " ", "world"] -> "Hello World" // ["hello", " ", "world"] -> "Hello World"
fn capitalize_words_string(words: &[&str]) -> String { fn capitalize_words_string(words: &[&str]) -> String {
// ??? words.iter().map(|i| {
capitalize_first(i)
}).collect::<Vec<String>>().join("")
} }
fn main() { fn main() {

View file

@ -1,3 +1,5 @@
use std::f32::consts::E;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
enum DivisionError { enum DivisionError {
// Example: 42 / 0 // Example: 42 / 0
@ -8,24 +10,43 @@ enum DivisionError {
NotDivisible, NotDivisible,
} }
// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
// Otherwise, return a suitable error. // Otherwise, return a suitable error.
fn divide(a: i64, b: i64) -> Result<i64, DivisionError> { fn divide(a: i64, b: i64) -> Result<i64, DivisionError> {
todo!(); if b == 0 {
return Err(DivisionError::DivideByZero)
}
match a.checked_rem(b) {
None => {
Err(DivisionError::IntegerOverflow)
}
Some(0) => {
let division = a.checked_div(b);
if let Some(result) = division {
Ok(result)
} else {
Err(DivisionError::IntegerOverflow)
}
},
Some(_) => {
Err(DivisionError::NotDivisible)
}
}
} }
// TODO: Add the correct return type and complete the function body.
// Desired output: `Ok([1, 11, 1426, 3])` // 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 numbers = [27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27)); let division_results = numbers.into_iter().map(|n| divide(n, 27));
Ok(division_results.map(|i| {
i.unwrap()
}).collect::<Vec<i64>>())
} }
// TODO: Add the correct return type and complete the function body.
// Desired output: `[Ok(1), Ok(11), Ok(1426), Ok(3)]` // 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 numbers = [27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27)); let division_results = numbers.into_iter().map(|n| divide(n, 27));
division_results.collect()
} }
fn main() { fn main() {

View file

@ -10,6 +10,9 @@ fn factorial(num: u64) -> u64 {
// - additional variables // - additional variables
// For an extra challenge, don't use: // For an extra challenge, don't use:
// - recursion // - recursion
(1..=num).fold(1, |acc, v| {
acc * v
})
} }
fn main() { fn main() {

View file

@ -23,11 +23,14 @@ fn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {
count count
} }
// TODO: Implement the functionality of `count_for` but with an iterator instead // Implement the functionality of `count_for` but with an iterator instead
// of a `for` loop. // of a `for` loop.
fn count_iterator(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` is a hash map with `String` keys and `Progress` values.
// map = { "variables1": Complete, "from_str": None, … } // map = { "variables1": Complete, "from_str": None, … }
map.iter().filter(|p| {
*p.1 == value
}).count()
} }
fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize { fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
@ -42,12 +45,13 @@ fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progres
count count
} }
// TODO: Implement the functionality of `count_collection_for` but with an // Implement the functionality of `count_collection_for` but with an
// iterator instead of a `for` loop. // iterator instead of a `for` loop.
fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize { fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
// `collection` is a slice of hash maps. // `collection` is a slice of hash maps.
// collection = [{ "variables1": Complete, "from_str": None, … }, // collection = [{ "variables1": Complete, "from_str": None, … },
// { "variables2": Complete, … }, … ] // { "variables2": Complete, … }, … ]
collection.iter().map(|i| {count_iterator(i, value)}).sum()
} }
fn main() { fn main() {

View file

@ -1,4 +1,28 @@
fn main() { // The Rust compiler needs to know how to check whether supplied references are
// DON'T EDIT THIS SOLUTION FILE! // valid, so that it can let the programmer know if a reference is at risk of
// It will be automatically filled after you finish the exercise. // going out of scope before it is used. Remember, references are borrows and do
// not own their own data. What if their owner goes out of scope?
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
// ^^^^ ^^ ^^ ^^
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_longest() {
assert_eq!(longest("abcd", "123"), "abcd");
assert_eq!(longest("abc", "1234"), "1234");
}
} }

View file

@ -1,4 +1,33 @@
fn main() { fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
// DON'T EDIT THIS SOLUTION FILE! if x.len() > y.len() {
// It will be automatically filled after you finish the exercise. x
} else {
y
}
}
fn main() {
let string1 = String::from("long string is long");
// Solution1: You can move `strings2` out of the inner block so that it is
// not dropped before the print statement.
let string2 = String::from("xyz");
let result;
{
result = longest(&string1, &string2);
}
println!("The longest string is '{result}'");
// `string2` dropped at the end of the function.
// =========================================================================
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(&string1, &string2);
// Solution2: You can move the print statement into the inner block so
// that it is executed before `string2` is dropped.
println!("The longest string is '{result}'");
// `string2` dropped here (end of the inner scope).
}
} }

View file

@ -1,4 +1,18 @@
fn main() { // Lifetimes are also needed when structs hold references.
// DON'T EDIT THIS SOLUTION FILE!
// It will be automatically filled after you finish the exercise. struct Book<'a> {
// ^^^^ added a lifetime annotation
author: &'a str,
// ^^
title: &'a str,
// ^^
}
fn main() {
let book = Book {
author: "George Orwell",
title: "1984",
};
println!("{} by {}", book.title, book.author);
} }

View file

@ -1,4 +1,24 @@
fn main() { // Tests are important to ensure that your code does what you think it should
// DON'T EDIT THIS SOLUTION FILE! // do.
// It will be automatically filled after you finish the exercise.
fn is_even(n: i64) -> bool {
n % 2 == 0
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
// When writing unit tests, it is common to import everything from the outer
// module (`super`) using a wildcard.
use super::*;
#[test]
fn you_can_assert() {
assert!(is_even(0));
assert!(!is_even(-1));
// ^ You can assert `false` using the negation operator `!`.
}
} }

View file

@ -1,4 +1,22 @@
fn main() { // Calculates the power of 2 using a bit shift.
// DON'T EDIT THIS SOLUTION FILE! // `1 << n` is equivalent to "2 to the power of n".
// It will be automatically filled after you finish the exercise. fn power_of_2(n: u8) -> u64 {
1 << n
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn you_can_assert_eq() {
assert_eq!(power_of_2(0), 1);
assert_eq!(power_of_2(1), 2);
assert_eq!(power_of_2(2), 4);
assert_eq!(power_of_2(3), 8);
}
} }

View file

@ -1,4 +1,45 @@
fn main() { struct Rectangle {
// DON'T EDIT THIS SOLUTION FILE! width: i32,
// It will be automatically filled after you finish the exercise. height: i32,
}
impl Rectangle {
// Don't change this function.
fn new(width: i32, height: i32) -> Self {
if width <= 0 || height <= 0 {
// Returning a `Result` would be better here. But we want to learn
// how to test functions that can panic.
panic!("Rectangle width and height must be positive");
}
Rectangle { width, height }
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn correct_width_and_height() {
let rect = Rectangle::new(10, 20);
assert_eq!(rect.width, 10); // Check width
assert_eq!(rect.height, 20); // Check height
}
#[test]
#[should_panic] // Added this attribute to check that the test panics.
fn negative_width() {
let _rect = Rectangle::new(-10, 10);
}
#[test]
#[should_panic] // Added this attribute to check that the test panics.
fn negative_height() {
let _rect = Rectangle::new(10, -10);
}
} }

View file

@ -1,4 +1,26 @@
// When performing operations on elements within a collection, iterators are
// essential. This module helps you get familiar with the structure of using an
// iterator and how to go through elements within an iterable collection.
fn main() { fn main() {
// DON'T EDIT THIS SOLUTION FILE! // You can optionally experiment here.
// It will be automatically filled after you finish the exercise. }
#[cfg(test)]
mod tests {
#[test]
fn iterators() {
let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"];
// Create an iterator over the array.
let mut fav_fruits_iterator = my_fav_fruits.iter();
assert_eq!(fav_fruits_iterator.next(), Some(&"banana"));
assert_eq!(fav_fruits_iterator.next(), Some(&"custard apple"));
assert_eq!(fav_fruits_iterator.next(), Some(&"avocado"));
assert_eq!(fav_fruits_iterator.next(), Some(&"peach"));
assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry"));
assert_eq!(fav_fruits_iterator.next(), None);
// ^^^^ reached the end
}
} }

View file

@ -1,4 +1,56 @@
fn main() { // In this exercise, you'll learn some of the unique advantages that iterators
// DON'T EDIT THIS SOLUTION FILE! // can offer.
// It will be automatically filled after you finish the exercise.
// "hello" -> "Hello"
fn capitalize_first(input: &str) -> String {
let mut chars = input.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().to_string() + chars.as_str(),
}
}
// Apply the `capitalize_first` function to a slice of string slices.
// Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"]
fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
words.iter().map(|word| capitalize_first(word)).collect()
}
// Apply the `capitalize_first` function again to a slice of string
// slices. Return a single string.
// ["hello", " ", "world"] -> "Hello World"
fn capitalize_words_string(words: &[&str]) -> String {
words.iter().map(|word| capitalize_first(word)).collect()
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
assert_eq!(capitalize_first("hello"), "Hello");
}
#[test]
fn test_empty() {
assert_eq!(capitalize_first(""), "");
}
#[test]
fn test_iterate_string_vec() {
let words = vec!["hello", "world"];
assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]);
}
#[test]
fn test_iterate_into_string() {
let words = vec!["hello", " ", "world"];
assert_eq!(capitalize_words_string(&words), "Hello World");
}
} }