mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-01-11 00:00:04 +03:00
Compare commits
No commits in common. "746cf6863dee8f676596b07e74bad1a19fa2579e" and "129884aff74964d13aba8309014554b5625d6e5b" have entirely different histories.
746cf6863d
...
129884aff7
9
.github/workflows/rust.yml
vendored
9
.github/workflows/rust.yml
vendored
|
@ -2,19 +2,14 @@ name: Rustlings Tests
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [ main ]
|
||||
|
||||
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:
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(???) -> Self { ??? }
|
||||
// TODO: add another error conversion function here.
|
||||
// fn from_parseint...
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!();
|
||||
}
|
||||
}
|
||||
|
|
45
exercises/17_tests/tests4.rs
Normal file
45
exercises/17_tests/tests4.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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 {}
|
||||
|
|
|
@ -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+"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
|
@ -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 🥰
|
||||
|
|
1
solutions/17_tests/tests4.rs
Normal file
1
solutions/17_tests/tests4.rs
Normal file
|
@ -0,0 +1 @@
|
|||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
|
@ -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 🥰
|
||||
|
|
Loading…
Reference in a new issue