Completed exercise up to Traits
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
DON'T EDIT THIS FILE!
|
||||
|
||||
quiz2
|
||||
quiz3
|
||||
|
||||
intro1
|
||||
intro2
|
||||
@@ -48,3 +48,20 @@ modules3
|
||||
hashmaps1
|
||||
hashmaps2
|
||||
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
|
||||
// `hour_of_day` is higher than 23.
|
||||
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() {
|
||||
@@ -20,7 +35,7 @@ mod tests {
|
||||
// Option?
|
||||
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]
|
||||
|
||||
@@ -10,7 +10,10 @@ mod tests {
|
||||
let optional_target = Some(target);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -29,10 +32,16 @@ mod tests {
|
||||
// TODO: Make this a while-let statement. Remember that `Vec::pop()`
|
||||
// adds another layer of `Option`. You can do nested pattern matching
|
||||
// in if-let and while-let statements.
|
||||
integer = optional_integers.pop() {
|
||||
assert_eq!(integer, cursor);
|
||||
while let Some(integer) = optional_integers.pop() {
|
||||
if let Some(integer_value) = integer {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ fn main() {
|
||||
|
||||
// TODO: Fix the compiler error by adding something to this match statement.
|
||||
match optional_point {
|
||||
Some(p) => println!("Coordinates are {},{}", p.x, p.y),
|
||||
Some(ref p) => println!("Coordinates are {},{}", p.x, p.y),
|
||||
_ => panic!("No match!"),
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
// construct to `Option` that can be used to express error conditions. Change
|
||||
// the function signature and body to return `Result<String, String>` instead
|
||||
// of `Option<String>`.
|
||||
fn generate_nametag_text(name: String) -> Option<String> {
|
||||
fn generate_nametag_text(name: String) -> Result<String, String> {
|
||||
if name.is_empty() {
|
||||
// Empty names aren't allowed
|
||||
None
|
||||
// None
|
||||
Err("Empty names aren't allowed".to_string())
|
||||
} 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;
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||
|
||||
// TODO: Fix the compiler error by changing the signature and body of the
|
||||
// `main` function.
|
||||
fn main() {
|
||||
fn main() -> Result<(), ParseIntError>{
|
||||
let mut tokens = 100;
|
||||
let pretend_user_input = "8";
|
||||
|
||||
@@ -28,4 +28,6 @@ fn main() {
|
||||
tokens -= cost;
|
||||
println!("You now have {tokens} tokens.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -11,8 +11,14 @@ impl PositiveNonzeroInteger {
|
||||
fn new(value: i64) -> Result<Self, CreationError> {
|
||||
// TODO: This function shouldn't always return an `Ok`.
|
||||
// Read the tests below to clarify what should be returned.
|
||||
if value < 0 {
|
||||
Err(CreationError::Negative)
|
||||
} else if value == 0 {
|
||||
Err(CreationError::Zero)
|
||||
} else {
|
||||
Ok(Self(value as u64))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -48,7 +48,7 @@ impl PositiveNonzeroInteger {
|
||||
|
||||
// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we
|
||||
// use to describe both errors? Is there a trait which both errors implement?
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<dyn Error>>{
|
||||
let pretend_user_input = "42";
|
||||
let x: i64 = pretend_user_input.parse()?;
|
||||
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
|
||||
|
||||
@@ -26,6 +26,9 @@ impl ParsePosNonzeroError {
|
||||
|
||||
// TODO: Add another error conversion function here.
|
||||
// fn from_parse_int(???) -> Self { ??? }
|
||||
fn from_parse_int(err: ParseIntError) -> Self {
|
||||
Self::ParseInt(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
@@ -43,7 +46,7 @@ impl PositiveNonzeroInteger {
|
||||
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
|
||||
// TODO: change this to return an appropriate error instead of panicking
|
||||
// when `parse()` returns an error.
|
||||
let x: i64 = s.parse().unwrap();
|
||||
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
|
||||
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ fn main() {
|
||||
// TODO: Fix the compiler error by annotating the type of the vector
|
||||
// `Vec<T>`. Choose `T` as some integer type that can be created from
|
||||
// `u8` and `i8`.
|
||||
let mut numbers = Vec::new();
|
||||
let mut numbers: Vec<i16> = Vec::new();
|
||||
|
||||
// Don't change the lines below.
|
||||
let n1: u8 = 42;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// This powerful wrapper provides the ability to store a positive integer value.
|
||||
// TODO: Rewrite it using a generic so that it supports wrapping ANY type.
|
||||
struct Wrapper {
|
||||
value: u32,
|
||||
struct Wrapper<T> {
|
||||
value: T,
|
||||
}
|
||||
|
||||
// TODO: Adapt the struct's implementation to be generic over the wrapped value.
|
||||
impl Wrapper {
|
||||
fn new(value: u32) -> Self {
|
||||
impl<T> Wrapper<T> {
|
||||
fn new(value: T) -> Self {
|
||||
Wrapper { value }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,14 @@ trait AppendBar {
|
||||
|
||||
impl AppendBar for 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() {
|
||||
|
||||
@@ -4,6 +4,13 @@ trait AppendBar {
|
||||
|
||||
// TODO: Implement the trait `AppendBar` for a vector of strings.
|
||||
// `append_bar` should push the string "Bar" into the vector.
|
||||
impl AppendBar for Vec<String> {
|
||||
// TODO: Implement `AppendBar` for the type `String`.
|
||||
fn append_bar(mut self) -> Self{
|
||||
self.push("Bar".to_string());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// You can optionally experiment here.
|
||||
|
||||
@@ -3,7 +3,9 @@ trait Licensed {
|
||||
// implementors like the two structs below can share that default behavior
|
||||
// without repeating the function.
|
||||
// The default license information should be the string "Default license".
|
||||
fn licensing_info(&self) -> String;
|
||||
fn licensing_info(&self) -> String {
|
||||
String::from("Default license")
|
||||
}
|
||||
}
|
||||
|
||||
struct SomeSoftware {
|
||||
|
||||
@@ -11,7 +11,8 @@ impl Licensed for SomeSoftware {}
|
||||
impl Licensed for OtherSoftware {}
|
||||
|
||||
// TODO: Fix the compiler error by only changing the signature of this function.
|
||||
fn compare_license_types(software1: ???, software2: ???) -> bool {
|
||||
fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {
|
||||
// Impl Licensed means to accept every types that implement Licensed
|
||||
software1.licensing_info() == software2.licensing_info()
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ impl SomeTrait for OtherStruct {}
|
||||
impl OtherTrait for OtherStruct {}
|
||||
|
||||
// TODO: Fix the compiler error by only changing the signature of this function.
|
||||
fn some_func(item: ???) -> bool {
|
||||
fn some_func(item: impl SomeTrait + OtherTrait) -> bool {
|
||||
item.some_function() && item.other_function()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,39 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// This function returns how much icecream there is left in the fridge.
|
||||
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// You can optionally experiment here.
|
||||
}
|
||||
|
||||
#[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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
#[derive(Debug)]
|
||||
struct Point {
|
||||
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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
fn generate_nametag_text(name: String) -> Result<String, String> {
|
||||
// ^^^^^^ ^^^^^^
|
||||
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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// Say we're writing a game where you can buy items with tokens. All items cost
|
||||
// 5 tokens, and whenever you purchase items there is a processing fee of 1
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// This is a program that is trying to use a completed version of the
|
||||
// `total_cost` function from the previous exercise. It's not working though!
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// This exercise is an altered version of the `errors4` exercise. It uses some
|
||||
// concepts that we won't get to until later in the course, like `Box` and the
|
||||
// `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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// 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_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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// `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:?}");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,28 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,27 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,36 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,35 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,39 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user