Completed exercise up to Traits
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
DON'T EDIT THIS FILE!
|
DON'T EDIT THIS FILE!
|
||||||
|
|
||||||
quiz2
|
quiz3
|
||||||
|
|
||||||
intro1
|
intro1
|
||||||
intro2
|
intro2
|
||||||
@@ -47,4 +47,21 @@ modules2
|
|||||||
modules3
|
modules3
|
||||||
hashmaps1
|
hashmaps1
|
||||||
hashmaps2
|
hashmaps2
|
||||||
hashmaps3
|
hashmaps3
|
||||||
|
quiz2
|
||||||
|
options1
|
||||||
|
options2
|
||||||
|
options3
|
||||||
|
errors1
|
||||||
|
errors2
|
||||||
|
errors3
|
||||||
|
errors4
|
||||||
|
errors5
|
||||||
|
errors6
|
||||||
|
generics1
|
||||||
|
generics2
|
||||||
|
traits1
|
||||||
|
traits2
|
||||||
|
traits3
|
||||||
|
traits4
|
||||||
|
traits5
|
||||||
@@ -3,7 +3,22 @@
|
|||||||
// someone eats it all, so no ice cream is left (value 0). Return `None` if
|
// someone eats it all, so no ice cream is left (value 0). Return `None` if
|
||||||
// `hour_of_day` is higher than 23.
|
// `hour_of_day` is higher than 23.
|
||||||
fn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {
|
fn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {
|
||||||
// TODO: Complete the function body.
|
if hour_of_day > 23 {
|
||||||
|
None
|
||||||
|
} else if hour_of_day < 22 {
|
||||||
|
Some(5)
|
||||||
|
} else {
|
||||||
|
Some(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OR
|
||||||
|
// fn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {
|
||||||
|
// match hour_of_day {
|
||||||
|
// 0..=21 => Some(5),
|
||||||
|
// 22 | 23 => Some(0),
|
||||||
|
// _ => None,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -20,7 +35,7 @@ mod tests {
|
|||||||
// Option?
|
// Option?
|
||||||
let ice_creams = maybe_ice_cream(12);
|
let ice_creams = maybe_ice_cream(12);
|
||||||
|
|
||||||
assert_eq!(ice_creams, 5); // Don't change this line.
|
assert_eq!(ice_creams, Some(5)); // Don't change this line.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ mod tests {
|
|||||||
let optional_target = Some(target);
|
let optional_target = Some(target);
|
||||||
|
|
||||||
// TODO: Make this an if-let statement whose value is `Some`.
|
// TODO: Make this an if-let statement whose value is `Some`.
|
||||||
word = optional_target {
|
// word = optional_target {
|
||||||
|
// assert_eq!(word, target);
|
||||||
|
// }
|
||||||
|
if let Some(word) = optional_target {
|
||||||
assert_eq!(word, target);
|
assert_eq!(word, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,10 +32,16 @@ mod tests {
|
|||||||
// TODO: Make this a while-let statement. Remember that `Vec::pop()`
|
// TODO: Make this a while-let statement. Remember that `Vec::pop()`
|
||||||
// adds another layer of `Option`. You can do nested pattern matching
|
// adds another layer of `Option`. You can do nested pattern matching
|
||||||
// in if-let and while-let statements.
|
// in if-let and while-let statements.
|
||||||
integer = optional_integers.pop() {
|
while let Some(integer) = optional_integers.pop() {
|
||||||
assert_eq!(integer, cursor);
|
if let Some(integer_value) = integer {
|
||||||
cursor -= 1;
|
println!("integer_value (left): {}, cursor (right): {}", integer_value, cursor);
|
||||||
|
assert_eq!(integer_value, cursor);
|
||||||
|
cursor -= 1;
|
||||||
|
} else {
|
||||||
|
println!("None value, cursor: {}", cursor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
assert_eq!(cursor, 0);
|
assert_eq!(cursor, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fn main() {
|
|||||||
|
|
||||||
// TODO: Fix the compiler error by adding something to this match statement.
|
// TODO: Fix the compiler error by adding something to this match statement.
|
||||||
match optional_point {
|
match optional_point {
|
||||||
Some(p) => println!("Coordinates are {},{}", p.x, p.y),
|
Some(ref p) => println!("Coordinates are {},{}", p.x, p.y),
|
||||||
_ => panic!("No match!"),
|
_ => panic!("No match!"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,14 @@
|
|||||||
// construct to `Option` that can be used to express error conditions. Change
|
// construct to `Option` that can be used to express error conditions. Change
|
||||||
// the function signature and body to return `Result<String, String>` instead
|
// the function signature and body to return `Result<String, String>` instead
|
||||||
// of `Option<String>`.
|
// of `Option<String>`.
|
||||||
fn generate_nametag_text(name: String) -> Option<String> {
|
fn generate_nametag_text(name: String) -> Result<String, String> {
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
// Empty names aren't allowed
|
// Empty names aren't allowed
|
||||||
None
|
// None
|
||||||
|
Err("Empty names aren't allowed".to_string())
|
||||||
} else {
|
} else {
|
||||||
Some(format!("Hi! My name is {name}"))
|
// Some(format!("Hi! My name is {name}"))
|
||||||
|
Ok(format!("Hi! My name is {name}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,14 @@ fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
|||||||
let cost_per_item = 5;
|
let cost_per_item = 5;
|
||||||
|
|
||||||
// TODO: Handle the error case as described above.
|
// TODO: Handle the error case as described above.
|
||||||
let qty = item_quantity.parse::<i32>();
|
let qty = item_quantity.parse::<i32>()?;
|
||||||
|
|
||||||
|
// OR
|
||||||
|
// let qty_result = item_quantity.parse::<i32>();
|
||||||
|
// let qty = match qty_result {
|
||||||
|
// Ok(number) => number,
|
||||||
|
// Err(e) => return Err(e),
|
||||||
|
// };
|
||||||
|
|
||||||
Ok(qty * cost_per_item + processing_fee)
|
Ok(qty * cost_per_item + processing_fee)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
|||||||
|
|
||||||
// TODO: Fix the compiler error by changing the signature and body of the
|
// TODO: Fix the compiler error by changing the signature and body of the
|
||||||
// `main` function.
|
// `main` function.
|
||||||
fn main() {
|
fn main() -> Result<(), ParseIntError>{
|
||||||
let mut tokens = 100;
|
let mut tokens = 100;
|
||||||
let pretend_user_input = "8";
|
let pretend_user_input = "8";
|
||||||
|
|
||||||
@@ -28,4 +28,6 @@ fn main() {
|
|||||||
tokens -= cost;
|
tokens -= cost;
|
||||||
println!("You now have {tokens} tokens.");
|
println!("You now have {tokens} tokens.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,13 @@ impl PositiveNonzeroInteger {
|
|||||||
fn new(value: i64) -> Result<Self, CreationError> {
|
fn new(value: i64) -> Result<Self, CreationError> {
|
||||||
// TODO: This function shouldn't always return an `Ok`.
|
// TODO: This function shouldn't always return an `Ok`.
|
||||||
// Read the tests below to clarify what should be returned.
|
// Read the tests below to clarify what should be returned.
|
||||||
Ok(Self(value as u64))
|
if value < 0 {
|
||||||
|
Err(CreationError::Negative)
|
||||||
|
} else if value == 0 {
|
||||||
|
Err(CreationError::Zero)
|
||||||
|
} else {
|
||||||
|
Ok(Self(value as u64))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ impl PositiveNonzeroInteger {
|
|||||||
|
|
||||||
// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we
|
// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we
|
||||||
// use to describe both errors? Is there a trait which both errors implement?
|
// use to describe both errors? Is there a trait which both errors implement?
|
||||||
fn main() {
|
fn main() -> Result<(), Box<dyn Error>>{
|
||||||
let pretend_user_input = "42";
|
let pretend_user_input = "42";
|
||||||
let x: i64 = pretend_user_input.parse()?;
|
let x: i64 = pretend_user_input.parse()?;
|
||||||
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
|
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ impl ParsePosNonzeroError {
|
|||||||
|
|
||||||
// TODO: Add another error conversion function here.
|
// TODO: Add another error conversion function here.
|
||||||
// fn from_parse_int(???) -> Self { ??? }
|
// fn from_parse_int(???) -> Self { ??? }
|
||||||
|
fn from_parse_int(err: ParseIntError) -> Self {
|
||||||
|
Self::ParseInt(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
@@ -43,7 +46,7 @@ impl PositiveNonzeroInteger {
|
|||||||
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
|
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
|
||||||
// TODO: change this to return an appropriate error instead of panicking
|
// TODO: change this to return an appropriate error instead of panicking
|
||||||
// when `parse()` returns an error.
|
// when `parse()` returns an error.
|
||||||
let x: i64 = s.parse().unwrap();
|
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
|
||||||
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ fn main() {
|
|||||||
// TODO: Fix the compiler error by annotating the type of the vector
|
// 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
|
// `Vec<T>`. Choose `T` as some integer type that can be created from
|
||||||
// `u8` and `i8`.
|
// `u8` and `i8`.
|
||||||
let mut numbers = Vec::new();
|
let mut numbers: Vec<i16> = Vec::new();
|
||||||
|
|
||||||
// Don't change the lines below.
|
// Don't change the lines below.
|
||||||
let n1: u8 = 42;
|
let n1: u8 = 42;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// This powerful wrapper provides the ability to store a positive integer value.
|
// 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.
|
// TODO: Rewrite it using a generic so that it supports wrapping ANY type.
|
||||||
struct Wrapper {
|
struct Wrapper<T> {
|
||||||
value: u32,
|
value: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Adapt the struct's implementation to be generic over the wrapped value.
|
// TODO: Adapt the struct's implementation to be generic over the wrapped value.
|
||||||
impl Wrapper {
|
impl<T> Wrapper<T> {
|
||||||
fn new(value: u32) -> Self {
|
fn new(value: T) -> Self {
|
||||||
Wrapper { value }
|
Wrapper { value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,14 @@ trait AppendBar {
|
|||||||
|
|
||||||
impl AppendBar for String {
|
impl AppendBar for String {
|
||||||
// TODO: Implement `AppendBar` for the type `String`.
|
// TODO: Implement `AppendBar` for the type `String`.
|
||||||
|
fn append_bar(self) -> Self{
|
||||||
|
// In this i need to put mut in front of the parameter
|
||||||
|
// self.push_str("Bar");
|
||||||
|
// self
|
||||||
|
|
||||||
|
// This way is more readable
|
||||||
|
self + "Bar"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@@ -4,6 +4,13 @@ trait AppendBar {
|
|||||||
|
|
||||||
// TODO: Implement the trait `AppendBar` for a vector of strings.
|
// TODO: Implement the trait `AppendBar` for a vector of strings.
|
||||||
// `append_bar` should push the string "Bar" into the vector.
|
// `append_bar` should push the string "Bar" into the vector.
|
||||||
|
impl AppendBar for Vec<String> {
|
||||||
|
// TODO: Implement `AppendBar` for the type `String`.
|
||||||
|
fn append_bar(mut self) -> Self{
|
||||||
|
self.push("Bar".to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// You can optionally experiment here.
|
// You can optionally experiment here.
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ trait Licensed {
|
|||||||
// implementors like the two structs below can share that default behavior
|
// implementors like the two structs below can share that default behavior
|
||||||
// without repeating the function.
|
// without repeating the function.
|
||||||
// The default license information should be the string "Default license".
|
// The default license information should be the string "Default license".
|
||||||
fn licensing_info(&self) -> String;
|
fn licensing_info(&self) -> String {
|
||||||
|
String::from("Default license")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SomeSoftware {
|
struct SomeSoftware {
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ impl Licensed for SomeSoftware {}
|
|||||||
impl Licensed for OtherSoftware {}
|
impl Licensed for OtherSoftware {}
|
||||||
|
|
||||||
// TODO: Fix the compiler error by only changing the signature of this function.
|
// TODO: Fix the compiler error by only changing the signature of this function.
|
||||||
fn compare_license_types(software1: ???, software2: ???) -> bool {
|
fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {
|
||||||
|
// Impl Licensed means to accept every types that implement Licensed
|
||||||
software1.licensing_info() == software2.licensing_info()
|
software1.licensing_info() == software2.licensing_info()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ impl SomeTrait for OtherStruct {}
|
|||||||
impl OtherTrait for OtherStruct {}
|
impl OtherTrait for OtherStruct {}
|
||||||
|
|
||||||
// TODO: Fix the compiler error by only changing the signature of this function.
|
// TODO: Fix the compiler error by only changing the signature of this function.
|
||||||
fn some_func(item: ???) -> bool {
|
fn some_func(item: impl SomeTrait + OtherTrait) -> bool {
|
||||||
item.some_function() && item.other_function()
|
item.some_function() && item.other_function()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,39 @@
|
|||||||
fn main() {
|
// This function returns how much icecream there is left in the fridge.
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
|
||||||
// It will be automatically filled after you finish the exercise.
|
// someone eats it all, so no icecream is left (value 0). Return `None` if
|
||||||
|
// `hour_of_day` is higher than 23.
|
||||||
|
fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
|
||||||
|
match hour_of_day {
|
||||||
|
0..=21 => Some(5),
|
||||||
|
22..=23 => Some(0),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn raw_value() {
|
||||||
|
// Using `unwrap` is fine in a test.
|
||||||
|
let icecreams = maybe_icecream(12).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(icecreams, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_icecream() {
|
||||||
|
assert_eq!(maybe_icecream(0), Some(5));
|
||||||
|
assert_eq!(maybe_icecream(9), Some(5));
|
||||||
|
assert_eq!(maybe_icecream(18), Some(5));
|
||||||
|
assert_eq!(maybe_icecream(22), Some(0));
|
||||||
|
assert_eq!(maybe_icecream(23), Some(0));
|
||||||
|
assert_eq!(maybe_icecream(24), None);
|
||||||
|
assert_eq!(maybe_icecream(25), None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,37 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// You can optionally experiment here.
|
||||||
// It will be automatically filled after you finish the exercise.
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn simple_option() {
|
||||||
|
let target = "rustlings";
|
||||||
|
let optional_target = Some(target);
|
||||||
|
|
||||||
|
// if-let
|
||||||
|
if let Some(word) = optional_target {
|
||||||
|
assert_eq!(word, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layered_option() {
|
||||||
|
let range = 10;
|
||||||
|
let mut optional_integers: Vec<Option<i8>> = vec![None];
|
||||||
|
|
||||||
|
for i in 1..=range {
|
||||||
|
optional_integers.push(Some(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cursor = range;
|
||||||
|
|
||||||
|
// while-let with nested pattern matching
|
||||||
|
while let Some(Some(integer)) = optional_integers.pop() {
|
||||||
|
assert_eq!(integer, cursor);
|
||||||
|
cursor -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(cursor, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,27 @@
|
|||||||
fn main() {
|
#[derive(Debug)]
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
struct Point {
|
||||||
// It will be automatically filled after you finish the exercise.
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let optional_point = Some(Point { x: 100, y: 200 });
|
||||||
|
|
||||||
|
// Solution 1: Matching over the `Option` (not `&Option`) but without moving
|
||||||
|
// out of the `Some` variant.
|
||||||
|
match optional_point {
|
||||||
|
Some(ref p) => println!("Coordinates are {},{}", p.x, p.y),
|
||||||
|
// ^^^ added
|
||||||
|
_ => panic!("No match!"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solution 2: Matching over a reference (`&Option`) by added `&` before
|
||||||
|
// `optional_point`.
|
||||||
|
match &optional_point {
|
||||||
|
//^ added
|
||||||
|
Some(p) => println!("Coordinates are {},{}", p.x, p.y),
|
||||||
|
_ => panic!("No match!"),
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{optional_point:?}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,37 @@
|
|||||||
fn main() {
|
fn generate_nametag_text(name: String) -> Result<String, String> {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// ^^^^^^ ^^^^^^
|
||||||
// It will be automatically filled after you finish the exercise.
|
if name.is_empty() {
|
||||||
|
// `Err(String)` instead of `None`.
|
||||||
|
Err("Empty names aren't allowed".to_string())
|
||||||
|
} else {
|
||||||
|
// `Ok` instead of `Some`.
|
||||||
|
Ok(format!("Hi! My name is {name}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generates_nametag_text_for_a_nonempty_name() {
|
||||||
|
assert_eq!(
|
||||||
|
generate_nametag_text("Beyoncé".to_string()).as_deref(),
|
||||||
|
Ok("Hi! My name is Beyoncé"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn explains_why_generating_nametag_text_fails() {
|
||||||
|
assert_eq!(
|
||||||
|
generate_nametag_text(String::new())
|
||||||
|
.as_ref()
|
||||||
|
.map_err(|e| e.as_str()),
|
||||||
|
Err("Empty names aren't allowed"),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,58 @@
|
|||||||
fn main() {
|
// Say we're writing a game where you can buy items with tokens. All items cost
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// 5 tokens, and whenever you purchase items there is a processing fee of 1
|
||||||
// It will be automatically filled after you finish the exercise.
|
// token. A player of the game will type in how many items they want to buy, and
|
||||||
|
// the `total_cost` function will calculate the total cost of the items. Since
|
||||||
|
// the player typed in the quantity, we get it as a string. They might have
|
||||||
|
// typed anything, not just numbers!
|
||||||
|
//
|
||||||
|
// Right now, this function isn't handling the error case at all. What we want
|
||||||
|
// to do is: If we call the `total_cost` function on a string that is not a
|
||||||
|
// number, that function will return a `ParseIntError`. In that case, we want to
|
||||||
|
// immediately return that error from our function and not try to multiply and
|
||||||
|
// add.
|
||||||
|
//
|
||||||
|
// There are at least two ways to implement this that are both correct. But one
|
||||||
|
// is a lot shorter!
|
||||||
|
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
#[allow(unused_variables, clippy::question_mark)]
|
||||||
|
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||||
|
let processing_fee = 1;
|
||||||
|
let cost_per_item = 5;
|
||||||
|
|
||||||
|
// Added `?` to propagate the error.
|
||||||
|
let qty = item_quantity.parse::<i32>()?;
|
||||||
|
// ^ added
|
||||||
|
|
||||||
|
// Equivalent to this verbose version:
|
||||||
|
let qty = match item_quantity.parse::<i32>() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(qty * cost_per_item + processing_fee)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::num::IntErrorKind;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn item_quantity_is_a_valid_number() {
|
||||||
|
assert_eq!(total_cost("34"), Ok(171));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn item_quantity_is_an_invalid_number() {
|
||||||
|
assert_eq!(
|
||||||
|
total_cost("beep boop").unwrap_err().kind(),
|
||||||
|
&IntErrorKind::InvalidDigit,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,32 @@
|
|||||||
fn main() {
|
// This is a program that is trying to use a completed version of the
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// `total_cost` function from the previous exercise. It's not working though!
|
||||||
// It will be automatically filled after you finish the exercise.
|
// Why not? What should we do to fix it?
|
||||||
|
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
// Don't change this function.
|
||||||
|
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||||
|
let processing_fee = 1;
|
||||||
|
let cost_per_item = 5;
|
||||||
|
let qty = item_quantity.parse::<i32>()?;
|
||||||
|
|
||||||
|
Ok(qty * cost_per_item + processing_fee)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), ParseIntError> {
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ added
|
||||||
|
let mut tokens = 100;
|
||||||
|
let pretend_user_input = "8";
|
||||||
|
|
||||||
|
let cost = total_cost(pretend_user_input)?;
|
||||||
|
|
||||||
|
if cost > tokens {
|
||||||
|
println!("You can't afford that many!");
|
||||||
|
} else {
|
||||||
|
tokens -= cost;
|
||||||
|
println!("You now have {tokens} tokens.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added this line to return the `Ok` variant of the expected `Result`.
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,42 @@
|
|||||||
fn main() {
|
use std::cmp::Ordering;
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
|
||||||
// It will be automatically filled after you finish the exercise.
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum CreationError {
|
||||||
|
Negative,
|
||||||
|
Zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
|
impl PositiveNonzeroInteger {
|
||||||
|
fn new(value: i64) -> Result<Self, CreationError> {
|
||||||
|
match value.cmp(&0) {
|
||||||
|
Ordering::Less => Err(CreationError::Negative),
|
||||||
|
Ordering::Equal => Err(CreationError::Zero),
|
||||||
|
Ordering::Greater => Ok(Self(value as u64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_creation() {
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::new(10),
|
||||||
|
Ok(PositiveNonzeroInteger(10)),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::new(-10),
|
||||||
|
Err(CreationError::Negative),
|
||||||
|
);
|
||||||
|
assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,54 @@
|
|||||||
fn main() {
|
// This exercise is an altered version of the `errors4` exercise. It uses some
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// concepts that we won't get to until later in the course, like `Box` and the
|
||||||
// It will be automatically filled after you finish the exercise.
|
// `From` trait. It's not important to understand them in detail right now, but
|
||||||
|
// you can read ahead if you like. For now, think of the `Box<dyn ???>` type as
|
||||||
|
// an "I want anything that does ???" type.
|
||||||
|
//
|
||||||
|
// In short, this particular use case for boxes is for when you want to own a
|
||||||
|
// value and you care only that it is a type which implements a particular
|
||||||
|
// trait. To do so, The `Box` is declared as of type `Box<dyn Trait>` where
|
||||||
|
// `Trait` is the trait the compiler looks for on any value used in that
|
||||||
|
// context. For this exercise, that context is the potential errors which
|
||||||
|
// can be returned in a `Result`.
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum CreationError {
|
||||||
|
Negative,
|
||||||
|
Zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is required so that `CreationError` can implement `Error`.
|
||||||
|
impl fmt::Display for CreationError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let description = match *self {
|
||||||
|
CreationError::Negative => "number is negative",
|
||||||
|
CreationError::Zero => "number is zero",
|
||||||
|
};
|
||||||
|
f.write_str(description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for CreationError {}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
|
impl PositiveNonzeroInteger {
|
||||||
|
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
||||||
|
match value {
|
||||||
|
x if x < 0 => Err(CreationError::Negative),
|
||||||
|
0 => Err(CreationError::Zero),
|
||||||
|
x => Ok(PositiveNonzeroInteger(x as u64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let pretend_user_input = "42";
|
||||||
|
let x: i64 = pretend_user_input.parse()?;
|
||||||
|
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,106 @@
|
|||||||
fn main() {
|
// Using catch-all error types like `Box<dyn Error>` isn't recommended for
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// library code where callers might want to make decisions based on the error
|
||||||
// It will be automatically filled after you finish the exercise.
|
// 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_parse_int(err: ParseIntError) -> Self {
|
||||||
|
Self::ParseInt(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// As an alternative solution, implementing the `From` trait allows for the
|
||||||
|
// automatic conversion from a `ParseIntError` into a `ParsePosNonzeroError`
|
||||||
|
// using the `?` operator, without the need to call `map_err`.
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// let x: i64 = s.parse()?;
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// Traits like `From` will be dealt with in later exercises.
|
||||||
|
impl From<ParseIntError> for ParsePosNonzeroError {
|
||||||
|
fn from(err: ParseIntError) -> Self {
|
||||||
|
ParsePosNonzeroError::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),
|
||||||
|
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_parse_int)?;
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,17 @@
|
|||||||
|
// `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() {
|
fn main() {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// `u8` and `i8` can both be converted to `i16`.
|
||||||
// It will be automatically filled after you finish the exercise.
|
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:?}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,28 @@
|
|||||||
fn main() {
|
struct Wrapper<T> {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
value: T,
|
||||||
// It will be automatically filled after you finish the exercise.
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,32 @@
|
|||||||
fn main() {
|
// The trait `AppendBar` has only one function which appends "Bar" to any object
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// implementing this trait.
|
||||||
// It will be automatically filled after you finish the exercise.
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,27 @@
|
|||||||
fn main() {
|
trait AppendBar {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
fn append_bar(self) -> Self;
|
||||||
// It will be automatically filled after you finish the exercise.
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,36 @@
|
|||||||
fn main() {
|
trait Licensed {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
fn licensing_info(&self) -> String {
|
||||||
// It will be automatically filled after you finish the exercise.
|
"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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,35 @@
|
|||||||
fn main() {
|
trait Licensed {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
fn licensing_info(&self) -> String {
|
||||||
// It will be automatically filled after you finish the exercise.
|
"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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,39 @@
|
|||||||
fn main() {
|
trait SomeTrait {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
fn some_function(&self) -> bool {
|
||||||
// It will be automatically filled after you finish the exercise.
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user