Compare commits

...

10 commits

Author SHA1 Message Date
mo8it 129884aff7 errors5 solution 2024-06-26 18:21:19 +02:00
mo8it 720b280bc1 Update deps 2024-06-26 16:59:13 +02:00
mo8it 9b7a5c041e errors4 solution 2024-06-26 15:54:18 +02:00
mo8it c46d8bdf95 errors3 solution 2024-06-26 15:44:33 +02:00
mo8it 050a23ce67 errors2 solution 2024-06-26 15:36:14 +02:00
mo8it 2afe6b38d3 Fix tests 2024-06-26 15:12:58 +02:00
mo8it 097f3c74ea errors1 solution 2024-06-26 15:06:29 +02:00
mo8it 25b5686dd2 options3 solution 2024-06-26 14:47:57 +02:00
mo8it a91888e79e option2 solution 2024-06-26 14:35:05 +02:00
mo8it c31e15c4cf options1 solution 2024-06-26 12:59:10 +02:00
19 changed files with 492 additions and 167 deletions

50
Cargo.lock generated
View file

@ -113,9 +113,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bstr"
@ -229,7 +229,7 @@ version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"crossterm_winapi",
"libc",
"mio",
@ -262,9 +262,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "either"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "equivalent"
@ -363,6 +363,15 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -450,7 +459,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"crossbeam-channel",
"filetime",
"fsevent-sys",
@ -559,9 +568,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.85"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@ -577,19 +586,20 @@ dependencies = [
[[package]]
name = "ratatui"
version = "0.26.3"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef"
checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cassowary",
"compact_str",
"crossterm",
"itertools",
"itertools 0.13.0",
"lru",
"paste",
"stability",
"strum",
"strum_macros",
"unicode-segmentation",
"unicode-truncate",
"unicode-width",
@ -610,7 +620,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
]
[[package]]
@ -719,9 +729,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.117"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
dependencies = [
"itoa",
"ryu",
@ -797,9 +807,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.26.2"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
dependencies = [
"strum_macros",
]
@ -819,9 +829,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.66"
version = "2.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
dependencies = [
"proc-macro2",
"quote",
@ -874,7 +884,7 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226"
dependencies = [
"itertools",
"itertools 0.12.1",
"unicode-width",
]

View file

@ -52,9 +52,9 @@ crossterm = "0.27.0"
hashbrown = "0.14.5"
notify-debouncer-mini = { version = "0.4.1", default-features = false }
os_pipe = "1.2.0"
ratatui = { version = "0.26.3", default-features = false, features = ["crossterm"] }
ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] }
rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" }
serde_json = "1.0.117"
serde_json = "1.0.118"
serde.workspace = true
toml_edit.workspace = true

View file

@ -1,12 +1,9 @@
// This function returns how much icecream there is left in the fridge.
// If it's before 10PM, there's 5 scoops left. At 10PM, someone eats it
// all, so there'll be no more left :(
fn maybe_icecream(time_of_day: u16) -> Option<u16> {
// We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a
// value of 0. The Option output should gracefully handle cases where
// time_of_day > 23.
// TODO: Complete the function body - remember to return an Option!
???
// 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> {
// TODO: Complete the function body.
}
fn main() {
@ -17,6 +14,14 @@ fn main() {
mod tests {
use super::*;
#[test]
fn raw_value() {
// TODO: Fix this test. How do you get the value contained in the
// Option?
let icecreams = maybe_icecream(12);
assert_eq!(icecreams, 5);
}
#[test]
fn check_icecream() {
assert_eq!(maybe_icecream(0), Some(5));
@ -24,14 +29,7 @@ mod tests {
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);
}
#[test]
fn raw_value() {
// TODO: Fix this test. How do you get at the value contained in the
// Option?
let icecreams = maybe_icecream(12);
assert_eq!(icecreams, 5);
}
}

View file

@ -9,7 +9,7 @@ mod tests {
let target = "rustlings";
let optional_target = Some(target);
// TODO: Make this an if let statement whose value is "Some" type
// TODO: Make this an if-let statement whose value is `Some`.
word = optional_target {
assert_eq!(word, target);
}
@ -20,15 +20,15 @@ mod tests {
let range = 10;
let mut optional_integers: Vec<Option<i8>> = vec![None];
for i in 1..(range + 1) {
for i in 1..=range {
optional_integers.push(Some(i));
}
let mut cursor = range;
// TODO: make this a while let statement - remember that vector.pop also
// adds another layer of Option<T>. You can stack `Option<T>`s into
// while let and if let.
// 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);
cursor -= 1;

View file

@ -1,14 +1,17 @@
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let y: Option<Point> = Some(Point { x: 100, y: 200 });
let optional_point = Some(Point { x: 100, y: 200 });
match y {
Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y),
_ => panic!("no match!"),
// TODO: Fix the compiler error by adding something to this match statement.
match optional_point {
Some(p) => println!("Co-ordinates are {},{}", p.x, p.y),
_ => panic!("No match!"),
}
y; // Fix without deleting this line.
println!("{optional_point:?}"); // Don't change this line.
}

View file

@ -1,22 +1,22 @@
// This function refuses to generate text to be printed on a nametag if you pass
// it an empty string. It'd be nicer if it explained what the problem was,
// instead of just sometimes returning `None`. Thankfully, Rust has a similar
// construct to `Option` that can be used to express error conditions. Let's use
// it!
fn main() {
// You can optionally experiment here.
}
// TODO: This function refuses to generate text to be printed on a nametag if
// you pass it an empty string. It'd be nicer if it explained what the problem
// was instead of just returning `None`. Thankfully, Rust has a similar
// 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> {
if name.is_empty() {
// Empty names aren't allowed.
None
} else {
Some(format!("Hi! My name is {}", name))
Some(format!("Hi! My name is {name}"))
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
@ -24,17 +24,18 @@ mod tests {
#[test]
fn generates_nametag_text_for_a_nonempty_name() {
assert_eq!(
generate_nametag_text("Beyoncé".into()),
Ok("Hi! My name is Beyoncé".into())
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("".into()),
// Don't change this line
Err("`name` was empty; it must be nonempty.".into())
generate_nametag_text(String::new())
.as_ref()
.map_err(|e| e.as_str()),
Err("Empty names aren't allowed"),
);
}
}

View file

@ -2,16 +2,16 @@
// 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, though, we get it as a string-- and they
// might have typed anything, not just numbers!
// 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 (and isn't
// handling the success case properly either). What we want to do is: if we call
// handling the success case properly either). 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`, and in that case, we want to immediately
// return that error from our function and not try to multiply and add.
// 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
// There are at least two ways to implement this that are both correct. But one
// is a lot shorter!
use std::num::ParseIntError;
@ -19,6 +19,8 @@ use std::num::ParseIntError;
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
// TODO: Handle the error case as described above.
let qty = item_quantity.parse::<i32>();
Ok(qty * cost_per_item + processing_fee)
@ -31,6 +33,7 @@ fn main() {
#[cfg(test)]
mod tests {
use super::*;
use std::num::IntErrorKind;
#[test]
fn item_quantity_is_a_valid_number() {
@ -40,8 +43,8 @@ mod tests {
#[test]
fn item_quantity_is_an_invalid_number() {
assert_eq!(
total_cost("beep boop").unwrap_err().to_string(),
"invalid digit found in string"
total_cost("beep boop").unwrap_err().kind(),
&IntErrorKind::InvalidDigit,
);
}
}

View file

@ -4,6 +4,17 @@
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)
}
// TODO: Fix the compiler error by changing the signature and body of the
// `main` function.
fn main() {
let mut tokens = 100;
let pretend_user_input = "8";
@ -14,14 +25,6 @@ fn main() {
println!("You can't afford that many!");
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
println!("You now have {tokens} tokens.");
}
}
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)
}

View file

@ -1,16 +1,16 @@
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
// Hmm... Why is this always returning an Ok value?
Ok(PositiveNonzeroInteger(value as u64))
fn new(value: i64) -> Result<Self, CreationError> {
// TODO: This function shouldn't always return an `Ok`.
Ok(Self(value as u64))
}
}
@ -24,11 +24,14 @@ mod tests {
#[test]
fn test_creation() {
assert!(PositiveNonzeroInteger::new(10).is_ok());
assert_eq!(
Err(CreationError::Negative),
PositiveNonzeroInteger::new(-10)
PositiveNonzeroInteger::new(10),
Ok(PositiveNonzeroInteger(10)),
);
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
assert_eq!(
PositiveNonzeroInteger::new(-10),
Err(CreationError::Negative),
);
assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero));
}
}

View file

@ -1,38 +1,18 @@
// This program uses an altered version of the code from errors4.
//
// This exercise 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, which,
// given Rust's usual standards for runtime safety, should strike you as
// somewhat lenient!
// 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.
//
// What can we use to describe both errors? In other words, is there a trait
// which both errors implement?
// 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;
use std::error::Error;
use std::fmt;
use std::num::ParseIntError;
// TODO: update the return type of `main()` to make this compile.
fn main() -> Result<(), Box<dyn ???>> {
let pretend_user_input = "42";
let x: i64 = pretend_user_input.parse()?;
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
Ok(())
}
// Don't change anything below this line.
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
#[derive(PartialEq, Debug)]
enum CreationError {
@ -40,17 +20,7 @@ enum CreationError {
Zero,
}
impl PositiveNonzeroInteger {
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(PositiveNonzeroInteger(x as u64)),
}
}
}
// This is required so that `CreationError` can implement `error::Error`.
// 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 {
@ -61,4 +31,26 @@ impl fmt::Display for CreationError {
}
}
impl error::Error for CreationError {}
impl Error for CreationError {}
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
match value {
0 => Err(CreationError::Zero),
x if x < 0 => Err(CreationError::Negative),
x => Ok(PositiveNonzeroInteger(x as u64)),
}
}
}
// 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() {
let pretend_user_input = "42";
let x: i64 = pretend_user_input.parse()?;
println!("output={:?}", PositiveNonzeroInteger::new(x)?);
Ok(())
}

View file

@ -603,7 +603,7 @@ hint = """
Options can have a `Some` value, with an inner value, or a `None` value,
without an inner value.
There's multiple ways to get at the inner value, you can use `unwrap`, or
There are multiple ways to get at the inner value, you can use `unwrap`, or
pattern match. Unwrapping is the easiest, but how do you do it safely so that
it doesn't panic in your face later?"""
@ -616,9 +616,9 @@ Check out:
- https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html
- https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html
Remember that `Option`s can be stacked in `if let` and `while let`.
Remember that `Option`s can be nested in if-let and while-let statements.
For example: `Some(Some(variable)) = variable2`
For example: `if let Some(Some(x)) = y`
Also see `Option::flatten`
"""
@ -631,7 +631,8 @@ hint = """
The compiler says a partial move happened in the `match` statement. How can
this be avoided? The compiler shows the correction needed.
After making the correction as suggested by the compiler, do read:
After making the correction as suggested by the compiler, read the related docs
page:
https://doc.rust-lang.org/std/keyword.ref.html"""
# ERROR HANDLING
@ -646,8 +647,8 @@ is that `generate_nametag_text` should return a `Result` instead of an `Option`.
To make this change, you'll need to:
- update the return type in the function signature to be a `Result<String,
String>` that could be the variants `Ok(String)` and `Err(String)`
- change the body of the function to return `Ok(stuff)` where it currently
returns `Some(stuff)`
- change the body of the function to return `Ok()` where it currently
returns `Some()`
- change the body of the function to return `Err(error message)` where it
currently returns `None`"""
@ -659,12 +660,11 @@ One way to handle this is using a `match` statement on
`item_quantity.parse::<i32>()` where the cases are `Ok(something)` and
`Err(something)`.
This pattern is very common in Rust, though, so there's a `?` operator that
This pattern is very common in Rust, though, so there's the `?` operator that
does pretty much what you would make that match statement do for you!
Take a look at this section of the 'Error Handling' chapter:
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
and give it a try!"""
Take a look at this section of the "Error Handling" chapter:
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator"""
[[exercises]]
name = "errors3"
@ -675,43 +675,40 @@ If other functions can return a `Result`, why shouldn't `main`? It's a fairly
common convention to return something like `Result<(), ErrorType>` from your
`main` function.
The unit (`()`) type is there because nothing is really needed in terms of
positive results."""
The unit type `()` is there because nothing is really needed in terms of a
positive result."""
[[exercises]]
name = "errors4"
dir = "13_error_handling"
hint = """
`PositiveNonzeroInteger::new` is always creating a new instance and returning
an `Ok` result.
It should be doing some checking, returning an `Err` result if those checks
fail, and only returning an `Ok` result if those checks determine that
everything is... okay :)"""
an `Ok` result. But it should be doing some checking, returning an `Err` if
those checks fail, and only returning an `Ok` if those checks determine that
everything is okay :)"""
[[exercises]]
name = "errors5"
dir = "13_error_handling"
test = false
hint = """
There are two different possible `Result` types produced within `main()`, which
are propagated using `?` operators. How do we declare a return type from
`main()` that allows both?
There are two different possible `Result` types produced within the `main`
function, which are propagated using the `?` operators. How do we declare a
return type for the `main` function that allows both?
Under the hood, the `?` operator calls `From::from` on the error value to
convert it to a boxed trait object, a `Box<dyn error::Error>`. This boxed trait
object is polymorphic, and since all errors implement the `error::Error` trait,
we can capture lots of different errors in one "Box" object.
convert it to a boxed trait object, a `Box<dyn Error>`. This boxed trait object
is polymorphic, and since all errors implement the `Error` trait, we can capture
lots of different errors in one `Box` object.
Check out this section of the book:
Check out this section of The Book:
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
Read more about boxing errors:
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
Read more about using the `?` operator with boxed errors:
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 = "errors6"

View file

@ -1 +1,38 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
// 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..22 => Some(5),
22..24 => 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);
}
}

View file

@ -1 +1,37 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
fn main() {
// 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);
}
}

View file

@ -1 +1,26 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
#[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!("Co-ordinates are {},{}", p.x, p.y),
// ^^^ added
_ => panic!("No match!"),
}
// Solution 2: Matching over a reference (`&Option`) by added `&` before
// `optional_point`.
match &optional_point {
Some(p) => println!("Co-ordinates are {},{}", p.x, p.y),
_ => panic!("No match!"),
}
println!("{optional_point:?}");
}

View file

@ -1 +1,37 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
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"),
);
}
}

View file

@ -1 +1,57 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
// 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 (and isn't
// handling the success case properly either). 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;
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,
);
}
}

View file

@ -1 +1,32 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
// 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(())
}

View file

@ -1 +1,42 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> {
if value == 0 {
Err(CreationError::Zero)
} else if value < 0 {
Err(CreationError::Negative)
} else {
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));
}
}

View file

@ -1 +1,54 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
// 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(())
}