Compare commits

..

No commits in common. "746cf6863dee8f676596b07e74bad1a19fa2579e" and "129884aff74964d13aba8309014554b5625d6e5b" have entirely different histories.

37 changed files with 294 additions and 790 deletions

View file

@ -10,11 +10,6 @@ env:
CARGO_TERM_COLOR: always
jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cargo clippy -- --deny warnings
fmt:
runs-on: ubuntu-latest
steps:

View file

@ -60,6 +60,8 @@ bin = [
{ name = "move_semantics4_sol", path = "../solutions/06_move_semantics/move_semantics4.rs" },
{ name = "move_semantics5", path = "../exercises/06_move_semantics/move_semantics5.rs" },
{ name = "move_semantics5_sol", path = "../solutions/06_move_semantics/move_semantics5.rs" },
{ name = "move_semantics6", path = "../exercises/06_move_semantics/move_semantics6.rs" },
{ name = "move_semantics6_sol", path = "../solutions/06_move_semantics/move_semantics6.rs" },
{ name = "structs1", path = "../exercises/07_structs/structs1.rs" },
{ name = "structs1_sol", path = "../solutions/07_structs/structs1.rs" },
{ name = "structs2", path = "../exercises/07_structs/structs2.rs" },
@ -140,6 +142,8 @@ bin = [
{ name = "tests2_sol", path = "../solutions/17_tests/tests2.rs" },
{ name = "tests3", path = "../exercises/17_tests/tests3.rs" },
{ name = "tests3_sol", path = "../solutions/17_tests/tests3.rs" },
{ name = "tests4", path = "../exercises/17_tests/tests4.rs" },
{ name = "tests4_sol", path = "../solutions/17_tests/tests4.rs" },
{ name = "iterators1", path = "../exercises/18_iterators/iterators1.rs" },
{ name = "iterators1_sol", path = "../solutions/18_iterators/iterators1.rs" },
{ name = "iterators2", path = "../exercises/18_iterators/iterators2.rs" },

View file

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

View file

@ -1,18 +1,12 @@
// Using catch-all error types like `Box<dyn Error>` isn't recommended for
// library code where callers might want to make decisions based on the error
// content instead of printing it out or propagating it further. Here, we define
// a custom error type to make it possible for callers to decide what to do next
// when our function returns an error.
// Using catch-all error types like `Box<dyn error::Error>` isn't recommended
// for library code, where callers might want to make decisions based on the
// error content, instead of printing it out or propagating it further. Here, we
// define a custom error type to make it possible for callers to decide what to
// do next when our function returns an error.
use std::num::ParseIntError;
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.
// This is a custom error type that we will be using in `parse_pos_nonzero()`.
#[derive(PartialEq, Debug)]
enum ParsePosNonzeroError {
Creation(CreationError),
@ -20,32 +14,39 @@ enum ParsePosNonzeroError {
}
impl ParsePosNonzeroError {
fn from_creation(err: CreationError) -> Self {
Self::Creation(err)
fn from_creation(err: CreationError) -> ParsePosNonzeroError {
ParsePosNonzeroError::Creation(err)
}
// TODO: add another error conversion function here.
// fn from_parseint...
}
// TODO: Add another error conversion function here.
// fn from_parseint(???) -> Self { ??? }
fn parse_pos_nonzero(s: &str) -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> {
// TODO: change this to return an appropriate error instead of panicking
// when `parse()` returns an error.
let x: i64 = s.parse().unwrap();
PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation)
}
// Don't change anything below this line.
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
match value {
x if x < 0 => Err(CreationError::Negative),
x if x == 0 => Err(CreationError::Zero),
x => Ok(Self(x as u64)),
x => Ok(PositiveNonzeroInteger(x as u64)),
}
}
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();
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
}
}
fn main() {
@ -55,36 +56,36 @@ fn main() {
#[cfg(test)]
mod test {
use super::*;
use std::num::IntErrorKind;
#[test]
fn test_parse_error() {
// We can't construct a ParseIntError, so we have to pattern match.
assert!(matches!(
PositiveNonzeroInteger::parse("not a number"),
Err(ParsePosNonzeroError::ParseInt(_)),
parse_pos_nonzero("not a number"),
Err(ParsePosNonzeroError::ParseInt(_))
));
}
#[test]
fn test_negative() {
assert_eq!(
PositiveNonzeroInteger::parse("-555"),
Err(ParsePosNonzeroError::Creation(CreationError::Negative)),
parse_pos_nonzero("-555"),
Err(ParsePosNonzeroError::Creation(CreationError::Negative))
);
}
#[test]
fn test_zero() {
assert_eq!(
PositiveNonzeroInteger::parse("0"),
Err(ParsePosNonzeroError::Creation(CreationError::Zero)),
parse_pos_nonzero("0"),
Err(ParsePosNonzeroError::Creation(CreationError::Zero))
);
}
#[test]
fn test_positive() {
let x = PositiveNonzeroInteger::new(42).unwrap();
assert_eq!(x.0, 42);
assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x));
let x = PositiveNonzeroInteger::new(42);
assert!(x.is_ok());
assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap()));
}
}

View file

@ -1,18 +1,7 @@
// `Vec<T>` is generic over the type `T`. In most cases, the compiler is able to
// infer `T`, for example after pushing a value with a concrete type to the vector.
// But in this exercise, the compiler needs some help through a type annotation.
// This shopping list program isn't compiling! Use your knowledge of generics to
// fix it.
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();
// Don't change the lines below.
let n1: u8 = 42;
numbers.push(n1.into());
let n2: i8 = -1;
numbers.push(n2.into());
println!("{numbers:?}");
let mut shopping_list: Vec<?> = Vec::new();
shopping_list.push("milk");
}

View file

@ -1,10 +1,10 @@
// 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.
// Rewrite it using generics so that it supports wrapping ANY type.
struct Wrapper {
value: u32,
}
// TODO: Adapt the struct's implementation to be generic over the wrapped value.
impl Wrapper {
fn new(value: u32) -> Self {
Wrapper { value }

View file

@ -1,17 +1,19 @@
// The trait `AppendBar` has only one function which appends "Bar" to any object
// implementing this trait.
// Time to implement some traits! Your task is to implement the trait
// `AppendBar` for the type `String`. The trait AppendBar has only one function,
// which appends "Bar" to any object implementing this trait.
trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for String {
// TODO: Implement `AppendBar` for the type `String`.
// TODO: Implement `AppendBar` for type `String`.
}
fn main() {
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {s}");
println!("s: {}", s);
}
#[cfg(test)]
@ -20,11 +22,14 @@ mod tests {
#[test]
fn is_foo_bar() {
assert_eq!(String::from("Foo").append_bar(), "FooBar");
assert_eq!(String::from("Foo").append_bar(), String::from("FooBar"));
}
#[test]
fn is_bar_bar() {
assert_eq!(String::from("").append_bar().append_bar(), "BarBar");
assert_eq!(
String::from("").append_bar().append_bar(),
String::from("BarBar")
);
}
}

View file

@ -1,9 +1,14 @@
// Your task is to implement the trait `AppendBar` for a vector of strings. To
// implement this trait, consider for a moment what it means to 'append "Bar"'
// to a vector of strings.
//
// No boiler plate code this time, you can do this!
trait AppendBar {
fn append_bar(self) -> Self;
}
// TODO: Implement the trait `AppendBar` for a vector of strings.
// `appned_bar` should push the string "Bar" into the vector.
// TODO: Implement trait `AppendBar` for a vector of strings.
fn main() {
// You can optionally experiment here.
@ -16,7 +21,7 @@ mod tests {
#[test]
fn is_vec_pop_eq_bar() {
let mut foo = vec![String::from("Foo")].append_bar();
assert_eq!(foo.pop().unwrap(), "Bar");
assert_eq!(foo.pop().unwrap(), "Foo");
assert_eq!(foo.pop().unwrap(), String::from("Bar"));
assert_eq!(foo.pop().unwrap(), String::from("Foo"));
}
}

View file

@ -1,8 +1,9 @@
// Your task is to implement the Licensed trait for both structures and have
// them return the same information without writing the same function twice.
//
// Consider what you can add to the Licensed trait.
trait Licensed {
// TODO: Add a default implementation for `licensing_info` so that
// implementors like the two structs below can share that default behavior
// without repeating the function.
// The default license information should be the string "Default license".
fn licensing_info(&self) -> String;
}
@ -14,8 +15,8 @@ struct OtherSoftware {
version_number: String,
}
impl Licensed for SomeSoftware {} // Don't edit this line.
impl Licensed for OtherSoftware {} // Don't edit this line.
impl Licensed for SomeSoftware {} // Don't edit this line
impl Licensed for OtherSoftware {} // Don't edit this line
fn main() {
// You can optionally experiment here.
@ -27,7 +28,7 @@ mod tests {
#[test]
fn is_licensing_info_the_same() {
let licensing_info = "Default license";
let licensing_info = String::from("Some information");
let some_software = SomeSoftware { version_number: 1 };
let other_software = OtherSoftware {
version_number: "v2.0.0".to_string(),

View file

@ -1,18 +1,23 @@
// Your task is to replace the '??' sections so the code compiles.
//
// Don't change any line other than the marked one.
trait Licensed {
fn licensing_info(&self) -> String {
"Default license".to_string()
"some information".to_string()
}
}
struct SomeSoftware;
struct OtherSoftware;
struct SomeSoftware {}
struct OtherSoftware {}
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 {
software1.licensing_info() == software2.licensing_info()
// YOU MAY ONLY CHANGE THE NEXT LINE
fn compare_license_types(software: ??, software_two: ??) -> bool {
software.licensing_info() == software_two.licensing_info()
}
fn main() {
@ -25,11 +30,17 @@ mod tests {
#[test]
fn compare_license_information() {
assert!(compare_license_types(SomeSoftware, OtherSoftware));
let some_software = SomeSoftware {};
let other_software = OtherSoftware {};
assert!(compare_license_types(some_software, other_software));
}
#[test]
fn compare_license_information_backwards() {
assert!(compare_license_types(OtherSoftware, SomeSoftware));
let some_software = SomeSoftware {};
let other_software = OtherSoftware {};
assert!(compare_license_types(other_software, some_software));
}
}

View file

@ -1,3 +1,7 @@
// Your task is to replace the '??' sections so the code compiles.
//
// Don't change any line other than the marked one.
trait SomeTrait {
fn some_function(&self) -> bool {
true
@ -10,30 +14,20 @@ trait OtherTrait {
}
}
struct SomeStruct;
struct SomeStruct {}
struct OtherStruct {}
impl SomeTrait for SomeStruct {}
impl OtherTrait for SomeStruct {}
struct OtherStruct;
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 {
// YOU MAY ONLY CHANGE THE NEXT LINE
fn some_func(item: ??) -> bool {
item.some_function() && item.other_function()
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_some_func() {
assert!(some_func(SomeStruct));
assert!(some_func(OtherStruct));
}
some_func(SomeStruct {});
some_func(OtherStruct {});
}

View file

@ -3,7 +3,6 @@
// going out of scope before it is used. Remember, references are borrows and do
// not own their own data. What if their owner goes out of scope?
// TODO: Fix the compiler error by updating the function signature.
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
@ -13,16 +12,9 @@ fn longest(x: &str, y: &str) -> &str {
}
fn main() {
// You can optionally experiment here.
}
let string1 = String::from("abcd");
let string2 = "xyz";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_longest() {
assert_eq!(longest("abcd", "123"), "abcd");
assert_eq!(longest("abc", "1234"), "1234");
}
let result = longest(string1.as_str(), string2);
println!("The longest string is '{}'", result);
}

View file

@ -1,4 +1,6 @@
// Don't change this function.
// So if the compiler is just validating the references passed to the annotated
// parameters and the return type, what do we need to change?
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
@ -8,13 +10,11 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
}
fn main() {
// TODO: Fix the compiler error by moving one line.
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(&string1, &string2);
result = longest(string1.as_str(), string2.as_str());
}
println!("The longest string is '{result}'");
println!("The longest string is '{}'", result);
}

View file

@ -1,15 +1,16 @@
// Lifetimes are also needed when structs hold references.
// TODO: Fix the compiler errors about the struct.
struct Book {
author: &str,
title: &str,
}
fn main() {
let name = String::from("Jill Smith");
let title = String::from("Fish Flying");
let book = Book {
author: "George Orwell",
title: "1984",
author: &name,
title: &title,
};
println!("{} by {}", book.title, book.author);

View file

@ -1,9 +1,9 @@
// Tests are important to ensure that your code does what you think it should
// do.
fn is_even(n: i64) -> bool {
n % 2 == 0
}
// do. Tests can be run on this file with the following command: rustlings run
// tests1
//
// This test has a problem with it -- make the test compile! Make the test pass!
// Make the test fail!
fn main() {
// You can optionally experiment here.
@ -11,13 +11,8 @@ fn main() {
#[cfg(test)]
mod tests {
// TODO: Import `is_even`. You can use a wildcard to import everything in
// the outer module.
#[test]
fn you_can_assert() {
// TODO: Test the function `is_even` with some values.
assert!();
assert!();
}
}

View file

@ -1,8 +1,5 @@
// Calculates the power of 2 using a bit shift.
// `1 << n` is equivalent to "2 to the power of n".
fn power_of_2(n: u8) -> u64 {
1 << n
}
// This test has a problem with it -- make the test compile! Make the test pass!
// Make the test fail!
fn main() {
// You can optionally experiment here.
@ -10,14 +7,8 @@ fn main() {
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn you_can_assert_eq() {
// TODO: Test the function `power_of_2` with some values.
assert_eq!();
assert_eq!();
assert_eq!();
assert_eq!();
}
}

View file

@ -1,19 +1,9 @@
struct Rectangle {
width: i32,
height: i32,
}
// This test isn't testing our function -- make it do that in such a way that
// the test passes. Then write a second test that tests whether we get the
// result we expect to get when we call `is_even(5)`.
impl Rectangle {
// Don't change this function.
fn new(width: i32, height: i32) -> Self {
if width <= 0 || height <= 0 {
// Returning a `Result` would be better here. But we want to learn
// how to test functions that can panic.
panic!("Rectangle width and height can't be negative");
}
Rectangle { width, height }
}
fn is_even(num: i32) -> bool {
num % 2 == 0
}
fn main() {
@ -25,25 +15,12 @@ mod tests {
use super::*;
#[test]
fn correct_width_and_height() {
// TODO: This test should check if the rectangle has the size that we
// pass to its constructor.
let rect = Rectangle::new(10, 20);
assert_eq!(???, 10); // Check width
assert_eq!(???, 20); // Check height
fn is_true_when_even() {
assert!();
}
// TODO: This test should check if the program panics when we try to create
// a rectangle with negative width.
#[test]
fn negative_width() {
let _rect = Rectangle::new(-10, 10);
}
// TODO: This test should check if the program panics when we try to create
// a rectangle with negative height.
#[test]
fn negative_height() {
let _rect = Rectangle::new(10, -10);
fn is_false_when_odd() {
assert!();
}
}

View file

@ -0,0 +1,45 @@
// Make sure that we're testing for the correct conditions!
struct Rectangle {
width: i32,
height: i32,
}
impl Rectangle {
// Only change the test functions themselves
fn new(width: i32, height: i32) -> Self {
if width <= 0 || height <= 0 {
panic!("Rectangle width and height cannot be negative!")
}
Rectangle { width, height }
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn correct_width_and_height() {
// This test should check if the rectangle is the size that we pass into its constructor
let rect = Rectangle::new(10, 20);
assert_eq!(???, 10); // check width
assert_eq!(???, 20); // check height
}
#[test]
fn negative_width() {
// This test should check if program panics when we try to create rectangle with negative width
let _rect = Rectangle::new(-10, 10);
}
#[test]
fn negative_height() {
// This test should check if program panics when we try to create rectangle with negative height
let _rect = Rectangle::new(10, -10);
}
}

View file

@ -33,7 +33,7 @@ impl Default for Person {
// 5. Extract the other element from the split operation and parse it into a
// `usize` as the age.
// If while parsing the age, something goes wrong, then return the default of
// Person. Otherwise, then return an instantiated Person object with the results
// Person Otherwise, then return an instantiated Person object with the results
impl From<&str> for Person {
fn from(s: &str) -> Person {}

View file

@ -3,27 +3,26 @@
// - Traits
//
// An imaginary magical school has a new report card generation system written
// in Rust! Currently, the system only supports creating report cards where the
// in Rust! Currently the system only supports creating report cards where the
// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the
// school also issues alphabetical grades (A+ -> F-) and needs to be able to
// print both types of report card!
//
// Make the necessary code changes in the struct `ReportCard` and the impl
// block to support alphabetical report cards in addition to numerical ones.
// Make the necessary code changes in the struct ReportCard and the impl block
// to support alphabetical report cards. Change the Grade in the second test to
// "A+" to show that your changes allow alphabetical grades.
// TODO: Adjust the struct as described above.
struct ReportCard {
grade: f32,
student_name: String,
student_age: u8,
}
// TODO: Adjust the impl block as described above.
impl ReportCard {
fn print(&self) -> String {
format!(
"{} ({}) - achieved a grade of {}",
&self.student_name, &self.student_age, &self.grade,
&self.student_name, &self.student_age, &self.grade
)
}
}
@ -45,20 +44,21 @@ mod tests {
};
assert_eq!(
report_card.print(),
"Tom Wriggle (12) - achieved a grade of 2.1",
"Tom Wriggle (12) - achieved a grade of 2.1"
);
}
#[test]
fn generate_alphabetic_report_card() {
// TODO: Make sure to change the grade here after you finish the exercise.
let report_card = ReportCard {
grade: "A+",
grade: 2.1,
student_name: "Gary Plotter".to_string(),
student_age: 11,
};
assert_eq!(
report_card.print(),
"Gary Plotter (11) - achieved a grade of A+",
"Gary Plotter (11) - achieved a grade of A+"
);
}
}

View file

@ -336,7 +336,7 @@ What do you think is the more commonly used pattern under Rust developers?
name = "move_semantics1"
dir = "06_move_semantics"
hint = """
So you've got the "cannot borrow `vec` as mutable, as it is not declared as mutable"
So you've got the "cannot borrow immutable local variable `vec` as mutable"
error on the line where we push an element to the vector, right?
The fix for this is going to be adding one keyword, and the addition is NOT on
@ -372,7 +372,7 @@ name = "move_semantics4"
dir = "06_move_semantics"
hint = """
Carefully reason about the range in which each mutable reference is in
scope. Does it help to update the value of `x` immediately after
scope. Does it help to update the value of referent (`x`) immediately after
the mutable reference is taken?
Read more about 'Mutable References' in the book's section 'References and Borrowing':
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.
@ -714,13 +714,17 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen
name = "errors6"
dir = "13_error_handling"
hint = """
This exercise uses a completed version of `PositiveNonzeroInteger` from the
previous exercises.
This exercise uses a completed version of `PositiveNonzeroInteger` from
errors4.
Below the line that `TODO` asks you to change, there is an example of using
the `map_err()` method on a `Result` to transform one type of error into
another. Try using something similar on the `Result` from `parse()`. You
can then use the `?` operator to return early.
might use the `?` operator to return early from the function, or you might
use a `match` expression, or maybe there's another way!
You can create another function inside `impl ParsePosNonzeroError` to use
with `map_err()`.
Read more about `map_err()` in the `std::result` documentation:
https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err"""
@ -734,20 +738,19 @@ test = false
hint = """
Vectors in Rust make use of generics to create dynamically sized arrays of any
type.
If the vector `numbers` has the type `Vec<T>`, then we can only push values of
type `T` to it. By using `into()` before pushing, we ask the compiler to convert
`n1` and `n2` to `T`. But the compiler doesn't know what `T` is yet and needs a
type annotation.
`u8` and `i8` can both be converted to `i16`, `i32` and `i64`. Choose one for
the generic of the vector."""
You need to tell the compiler what type we are pushing onto this vector."""
[[exercises]]
name = "generics2"
dir = "14_generics"
hint = """
Related section in The Book:
https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions"""
Currently we are wrapping only values of type `u32`.
Maybe we could update the explicit references to this data type somehow?
If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions
"""
# TRAITS
@ -755,49 +758,53 @@ https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions"
name = "traits1"
dir = "15_traits"
hint = """
More about traits in The Book:
https://doc.rust-lang.org/book/ch10-02-traits.html"""
A discussion about Traits in Rust can be found at:
https://doc.rust-lang.org/book/ch10-02-traits.html
"""
[[exercises]]
name = "traits2"
dir = "15_traits"
hint = """
Notice how the trait takes ownership of `self` and returns `Self`.
Notice how the trait takes ownership of `self`, and returns `Self`.
Although the signature of `append_bar` in the trait takes `self` as argument,
the implementation can take `mut self` instead. This is possible because the
the value is owned anyway."""
Try mutating the incoming string vector. Have a look at the tests to see
what the result should look like!
Vectors provide suitable methods for adding an element at the end. See
the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html"""
[[exercises]]
name = "traits3"
dir = "15_traits"
hint = """
Traits can have a default implementation for functions. Data types that
implement the trait can then use the default version of these functions
if they choose not to implement the function themselves.
Traits can have a default implementation for functions. Structs that implement
the trait can then use the default version of these functions if they choose not
to implement the function themselves.
Related section in The Book:
https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations"""
See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations
"""
[[exercises]]
name = "traits4"
dir = "15_traits"
hint = """
Instead of using concrete types as parameters you can use traits. Try replacing
`???` with `impl [what goes here?]`.
the '??' with 'impl <what goes here?>'
Related section in The Book:
https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters"""
See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
"""
[[exercises]]
name = "traits5"
dir = "15_traits"
test = false
hint = """
To ensure a parameter implements multiple traits use the '+ syntax'. Try
replacing `???` with 'impl [what goes here?] + [what goes here?]'.
replacing the '??' with 'impl <> + <>'.
Related section in The Book:
https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax"""
See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax
"""
# QUIZ 3
@ -805,24 +812,19 @@ https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bou
name = "quiz3"
dir = "quizzes"
hint = """
To find the best solution to this challenge, you need to recall your knowledge
of traits, specifically "Trait Bound Syntax":
https://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax
To find the best solution to this challenge you're going to need to think back
to your knowledge of traits, specifically 'Trait Bound Syntax'
Here is how to specify a trait bound for an implementation block:
`impl<T: Trait1 + Trait2 + > for Foo<T> { }`
You may need this:
`use std::fmt::Display;`
"""
You may also need this: `use std::fmt::Display;`."""
# LIFETIMES
[[exercises]]
name = "lifetimes1"
dir = "16_lifetimes"
test = false
hint = """
Let the compiler guide you. Also take a look at The Book if you need help:
Let the compiler guide you. Also take a look at the book if you need help:
https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html"""
[[exercises]]
@ -843,7 +845,9 @@ inner block:
name = "lifetimes3"
dir = "16_lifetimes"
test = false
hint = """Let the compiler guide you :)"""
hint = """
If you use a lifetime annotation in a struct's fields, where else does it need
to be added?"""
# TESTS
@ -851,31 +855,45 @@ hint = """Let the compiler guide you :)"""
name = "tests1"
dir = "17_tests"
hint = """
You don't even need to write any code to test -- you can just test values and
run that, even though you wouldn't do that in real life. :)
`assert!` is a macro that needs an argument. Depending on the value of the
argument, `assert!` will do nothing (in which case the test will pass) or
`assert!` will panic (in which case the test will fail).
So try giving different values to `assert!` and see which ones compile, which
ones pass, and which ones fail :)
If you want to check for `false`, you can negate the result of what you're
checking using `!`, like `assert!(!)`."""
ones pass, and which ones fail :)"""
[[exercises]]
name = "tests2"
dir = "17_tests"
hint = """
Like the previous exercise, you don't need to write any code to get this test
to compile and run.
`assert_eq!` is a macro that takes two arguments and compares them. Try giving
it two values that are equal! Try giving it two arguments that are different!
Try switching which argument comes first and which comes second!"""
Try giving it two values that are of different types! Try switching which
argument comes first and which comes second!"""
[[exercises]]
name = "tests3"
dir = "17_tests"
hint = """
We expect the method `Rectangle::new` to panic for negative values.
You can call a function right where you're passing arguments to `assert!`. So
you could do something like `assert!(having_fun())`.
To handle that, you need to add a special attribute to the test function.
If you want to check that you indeed get `false`, you can negate the result of
what you're doing using `!`, like `assert!(!having_fun())`."""
[[exercises]]
name = "tests4"
dir = "17_tests"
hint = """
We expect method `Rectangle::new()` to panic for negative values.
To handle that you need to add a special attribute to the test function.
You can refer to the docs:
https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
@ -901,7 +919,8 @@ Step 4:
An iterator goes through all elements in a collection, but what if we've run
out of elements? What should we expect here? If you're stuck, take a look at
https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas."""
https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas.
"""
[[exercises]]
name = "iterators2"
@ -995,7 +1014,8 @@ assertions).
For a non-empty list keep in mind that we want to use our `Cons` "list builder".
Although the current list is one of integers (`i32`), feel free to change the
definition and try other types!"""
definition and try other types!
"""
[[exercises]]
name = "rc1"
@ -1012,7 +1032,8 @@ In the end the `Sun` only has one reference again, to itself.
See more at: https://doc.rust-lang.org/book/ch15-04-rc.html
* Unfortunately Pluto is no longer considered a planet :("""
* Unfortunately Pluto is no longer considered a planet :(
"""
[[exercises]]
name = "arc1"
@ -1027,9 +1048,10 @@ inside the loop but still in the main thread.
thread-local copy of the numbers.
This is a simple exercise if you understand the underlying concepts, but if this
is too much of a struggle, consider reading through all of Chapter 16 in The
Book:
https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html"""
is too much of a struggle, consider reading through all of Chapter 16 in the
book:
https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html
"""
[[exercises]]
name = "cow1"
@ -1039,7 +1061,8 @@ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is
called.
Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation
on the `Cow` type."""
on the `Cow` type.
"""
# THREADS
@ -1058,7 +1081,8 @@ https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-f
Use the `JoinHandle`s to wait for each thread to finish and collect their
results.
https://doc.rust-lang.org/std/thread/struct.JoinHandle.html"""
https://doc.rust-lang.org/std/thread/struct.JoinHandle.html
"""
[[exercises]]
name = "threads2"
@ -1079,7 +1103,8 @@ let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
```
Similar to the code in the following example in the book:
https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads"""
https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads
"""
[[exercises]]
name = "threads3"
@ -1094,7 +1119,8 @@ one thread and receive them in another.
Multiple producers are possible by using clone() to create a duplicate of the
original sending end.
See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info."""
See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info.
"""
# MACROS
@ -1210,7 +1236,8 @@ or a closure to wrap the error from `parse::<usize>`.
Yet another hint: If you would like to propagate errors by using the `?`
operator in your solution, you might want to look at
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html"""
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
"""
[[exercises]]
name = "try_from_into"

View file

@ -1,92 +1 @@
// Using catch-all error types like `Box<dyn Error>` isn't recommended for
// library code where callers might want to make decisions based on the error
// content instead of printing it out or propagating it further. Here, we define
// a custom error type to make it possible for callers to decide what to do next
// when our function returns an error.
use std::num::ParseIntError;
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.
#[derive(PartialEq, Debug)]
enum ParsePosNonzeroError {
Creation(CreationError),
ParseInt(ParseIntError),
}
impl ParsePosNonzeroError {
fn from_creation(err: CreationError) -> Self {
Self::Creation(err)
}
fn from_parseint(err: ParseIntError) -> Self {
Self::ParseInt(err)
}
}
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> {
match value {
x if x < 0 => Err(CreationError::Negative),
x if x == 0 => Err(CreationError::Zero),
x => Ok(Self(x as u64)),
}
}
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
// Return an appropriate error instead of panicking when `parse()`
// returns an error.
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod test {
use super::*;
use std::num::IntErrorKind;
#[test]
fn test_parse_error() {
assert!(matches!(
PositiveNonzeroInteger::parse("not a number"),
Err(ParsePosNonzeroError::ParseInt(_)),
));
}
#[test]
fn test_negative() {
assert_eq!(
PositiveNonzeroInteger::parse("-555"),
Err(ParsePosNonzeroError::Creation(CreationError::Negative)),
);
}
#[test]
fn test_zero() {
assert_eq!(
PositiveNonzeroInteger::parse("0"),
Err(ParsePosNonzeroError::Creation(CreationError::Zero)),
);
}
#[test]
fn test_positive() {
let x = PositiveNonzeroInteger::new(42).unwrap();
assert_eq!(x.0, 42);
assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x));
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,17 +1 @@
// `Vec<T>` is generic over the type `T`. In most cases, the compiler is able to
// infer `T`, for example after pushing a value with a concrete type to the vector.
// But in this exercise, the compiler needs some help through a type annotation.
fn main() {
// `u8` and `i8` can both be converted to `i16`.
let mut numbers: Vec<i16> = Vec::new();
// ^^^^^^^^^^ added
// Don't change the lines below.
let n1: u8 = 42;
numbers.push(n1.into());
let n2: i8 = -1;
numbers.push(n2.into());
println!("{numbers:?}");
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,28 +1 @@
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
fn new(value: T) -> Self {
Wrapper { value }
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn store_u32_in_wrapper() {
assert_eq!(Wrapper::new(42).value, 42);
}
#[test]
fn store_str_in_wrapper() {
assert_eq!(Wrapper::new("Foo").value, "Foo");
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,32 +1 @@
// The trait `AppendBar` has only one function which appends "Bar" to any object
// implementing this trait.
trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for String {
fn append_bar(self) -> Self {
self + "Bar"
}
}
fn main() {
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {s}");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_foo_bar() {
assert_eq!(String::from("Foo").append_bar(), "FooBar");
}
#[test]
fn is_bar_bar() {
assert_eq!(String::from("").append_bar().append_bar(), "BarBar");
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,27 +1 @@
trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for Vec<String> {
fn append_bar(mut self) -> Self {
// ^^^ this is important
self.push(String::from("Bar"));
self
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_vec_pop_eq_bar() {
let mut foo = vec![String::from("Foo")].append_bar();
assert_eq!(foo.pop().unwrap(), "Bar");
assert_eq!(foo.pop().unwrap(), "Foo");
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,36 +1 @@
trait Licensed {
fn licensing_info(&self) -> String {
"Default license".to_string()
}
}
struct SomeSoftware {
version_number: i32,
}
struct OtherSoftware {
version_number: String,
}
impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_licensing_info_the_same() {
let licensing_info = "Default license";
let some_software = SomeSoftware { version_number: 1 };
let other_software = OtherSoftware {
version_number: "v2.0.0".to_string(),
};
assert_eq!(some_software.licensing_info(), licensing_info);
assert_eq!(other_software.licensing_info(), licensing_info);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,35 +1 @@
trait Licensed {
fn licensing_info(&self) -> String {
"Default license".to_string()
}
}
struct SomeSoftware;
struct OtherSoftware;
impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}
fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {
// ^^^^^^^^^^^^^ ^^^^^^^^^^^^^
software1.licensing_info() == software2.licensing_info()
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compare_license_information() {
assert!(compare_license_types(SomeSoftware, OtherSoftware));
}
#[test]
fn compare_license_information_backwards() {
assert!(compare_license_types(OtherSoftware, SomeSoftware));
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,39 +1 @@
trait SomeTrait {
fn some_function(&self) -> bool {
true
}
}
trait OtherTrait {
fn other_function(&self) -> bool {
true
}
}
struct SomeStruct;
impl SomeTrait for SomeStruct {}
impl OtherTrait for SomeStruct {}
struct OtherStruct;
impl SomeTrait for OtherStruct {}
impl OtherTrait for OtherStruct {}
fn some_func(item: impl SomeTrait + OtherTrait) -> bool {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
item.some_function() && item.other_function()
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_some_func() {
assert!(some_func(SomeStruct));
assert!(some_func(OtherStruct));
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

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

View file

@ -1,33 +1 @@
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
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).
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,18 +1 @@
// Lifetimes are also needed when structs hold references.
struct Book<'a> {
// ^^^^ added a lifetime annotation
author: &'a str,
// ^^
title: &'a str,
// ^^
}
fn main() {
let book = Book {
author: "George Orwell",
title: "1984",
};
println!("{} by {}", book.title, book.author);
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,24 +1 @@
// Tests are important to ensure that your code does what you think it should
// do.
fn is_even(n: i64) -> bool {
n % 2 == 0
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
// When writing unit tests, it is common to import everything from the outer
// module (`super`) using a wildcard.
use super::*;
#[test]
fn you_can_assert() {
assert!(is_even(0));
assert!(!is_even(-1));
// ^ You can assert `false` using the negation operator `!`.
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,22 +1 @@
// Calculates the power of 2 using a bit shift.
// `1 << n` is equivalent to "2 to the power of n".
fn power_of_2(n: u8) -> u64 {
1 << n
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn you_can_assert_eq() {
assert_eq!(power_of_2(0), 1);
assert_eq!(power_of_2(1), 2);
assert_eq!(power_of_2(2), 4);
assert_eq!(power_of_2(3), 8);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,45 +1 @@
struct Rectangle {
width: i32,
height: i32,
}
impl Rectangle {
// Don't change this function.
fn new(width: i32, height: i32) -> Self {
if width <= 0 || height <= 0 {
// Returning a `Result` would be better here. But we want to learn
// how to test functions that can panic.
panic!("Rectangle width and height can't be negative");
}
Rectangle { width, height }
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn correct_width_and_height() {
let rect = Rectangle::new(10, 20);
assert_eq!(rect.width, 10); // Check width
assert_eq!(rect.height, 20); // Check height
}
#[test]
#[should_panic] // Added this attribute to check that the test panics.
fn negative_width() {
let _rect = Rectangle::new(-10, 10);
}
#[test]
#[should_panic] // Added this attribute to check that the test panics.
fn negative_height() {
let _rect = Rectangle::new(10, -10);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -0,0 +1 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,69 +1 @@
// This quiz tests:
// - Generics
// - Traits
//
// An imaginary magical school has a new report card generation system written
// in Rust! Currently, the system only supports creating report cards where the
// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the
// school also issues alphabetical grades (A+ -> F-) and needs to be able to
// print both types of report card!
//
// Make the necessary code changes in the struct `ReportCard` and the impl
// block to support alphabetical report cards in addition to numerical ones.
use std::fmt::Display;
// Make the struct generic over `T`.
struct ReportCard<T> {
// ^^^
grade: T,
// ^
student_name: String,
student_age: u8,
}
// To be able to print the grade, it has to implement the `Display` trait.
impl<T: Display> ReportCard<T> {
// ^^^^^^^ require that `T` implements `Display`.
fn print(&self) -> String {
format!(
"{} ({}) - achieved a grade of {}",
&self.student_name, &self.student_age, &self.grade,
)
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generate_numeric_report_card() {
let report_card = ReportCard {
grade: 2.1,
student_name: "Tom Wriggle".to_string(),
student_age: 12,
};
assert_eq!(
report_card.print(),
"Tom Wriggle (12) - achieved a grade of 2.1",
);
}
#[test]
fn generate_alphabetic_report_card() {
let report_card = ReportCard {
grade: "A+",
student_name: "Gary Plotter".to_string(),
student_age: 11,
};
assert_eq!(
report_card.print(),
"Gary Plotter (11) - achieved a grade of A+",
);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰