mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-01-11 00:00:04 +03:00
Compare commits
10 commits
1694682aa4
...
129884aff7
Author | SHA1 | Date | |
---|---|---|---|
129884aff7 | |||
720b280bc1 | |||
9b7a5c041e | |||
c46d8bdf95 | |||
050a23ce67 | |||
2afe6b38d3 | |||
097f3c74ea | |||
25b5686dd2 | |||
a91888e79e | |||
c31e15c4cf |
50
Cargo.lock
generated
50
Cargo.lock
generated
|
@ -113,9 +113,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
|
@ -229,7 +229,7 @@ version = "0.27.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
|
@ -262,9 +262,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.12.0"
|
version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
|
@ -363,6 +363,15 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
@ -450,7 +459,7 @@ version = "6.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"filetime",
|
"filetime",
|
||||||
"fsevent-sys",
|
"fsevent-sys",
|
||||||
|
@ -559,9 +568,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.85"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -577,19 +586,20 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ratatui"
|
name = "ratatui"
|
||||||
version = "0.26.3"
|
version = "0.27.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef"
|
checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"cassowary",
|
"cassowary",
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"itertools",
|
"itertools 0.13.0",
|
||||||
"lru",
|
"lru",
|
||||||
"paste",
|
"paste",
|
||||||
"stability",
|
"stability",
|
||||||
"strum",
|
"strum",
|
||||||
|
"strum_macros",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-truncate",
|
"unicode-truncate",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
|
@ -610,7 +620,7 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -719,9 +729,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.117"
|
version = "1.0.118"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -797,9 +807,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
version = "0.26.2"
|
version = "0.26.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
|
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
]
|
]
|
||||||
|
@ -819,9 +829,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.66"
|
version = "2.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -874,7 +884,7 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226"
|
checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools 0.12.1",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -52,9 +52,9 @@ crossterm = "0.27.0"
|
||||||
hashbrown = "0.14.5"
|
hashbrown = "0.14.5"
|
||||||
notify-debouncer-mini = { version = "0.4.1", default-features = false }
|
notify-debouncer-mini = { version = "0.4.1", default-features = false }
|
||||||
os_pipe = "1.2.0"
|
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" }
|
rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" }
|
||||||
serde_json = "1.0.117"
|
serde_json = "1.0.118"
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
toml_edit.workspace = true
|
toml_edit.workspace = true
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
// This function returns how much icecream there is left in the fridge.
|
// 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
|
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
|
||||||
// all, so there'll be no more left :(
|
// someone eats it all, so no icecream is left (value 0). Return `None` if
|
||||||
fn maybe_icecream(time_of_day: u16) -> Option<u16> {
|
// `hour_of_day` is higher than 23.
|
||||||
// We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a
|
fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
|
||||||
// value of 0. The Option output should gracefully handle cases where
|
// TODO: Complete the function body.
|
||||||
// time_of_day > 23.
|
|
||||||
// TODO: Complete the function body - remember to return an Option!
|
|
||||||
???
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -17,6 +14,14 @@ fn main() {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn check_icecream() {
|
fn check_icecream() {
|
||||||
assert_eq!(maybe_icecream(0), Some(5));
|
assert_eq!(maybe_icecream(0), Some(5));
|
||||||
|
@ -24,14 +29,7 @@ mod tests {
|
||||||
assert_eq!(maybe_icecream(18), Some(5));
|
assert_eq!(maybe_icecream(18), Some(5));
|
||||||
assert_eq!(maybe_icecream(22), Some(0));
|
assert_eq!(maybe_icecream(22), Some(0));
|
||||||
assert_eq!(maybe_icecream(23), Some(0));
|
assert_eq!(maybe_icecream(23), Some(0));
|
||||||
|
assert_eq!(maybe_icecream(24), None);
|
||||||
assert_eq!(maybe_icecream(25), 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ mod tests {
|
||||||
let target = "rustlings";
|
let target = "rustlings";
|
||||||
let optional_target = Some(target);
|
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 {
|
word = optional_target {
|
||||||
assert_eq!(word, target);
|
assert_eq!(word, target);
|
||||||
}
|
}
|
||||||
|
@ -20,15 +20,15 @@ mod tests {
|
||||||
let range = 10;
|
let range = 10;
|
||||||
let mut optional_integers: Vec<Option<i8>> = vec![None];
|
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));
|
optional_integers.push(Some(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cursor = range;
|
let mut cursor = range;
|
||||||
|
|
||||||
// TODO: make this a while let statement - remember that vector.pop also
|
// TODO: Make this a while-let statement. Remember that `Vec::pop()`
|
||||||
// adds another layer of Option<T>. You can stack `Option<T>`s into
|
// adds another layer of `Option`. You can do nested pattern matching
|
||||||
// while let and if let.
|
// in if-let and while-let statements.
|
||||||
integer = optional_integers.pop() {
|
integer = optional_integers.pop() {
|
||||||
assert_eq!(integer, cursor);
|
assert_eq!(integer, cursor);
|
||||||
cursor -= 1;
|
cursor -= 1;
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
|
#[derive(Debug)]
|
||||||
struct Point {
|
struct Point {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let y: Option<Point> = Some(Point { x: 100, y: 200 });
|
let optional_point = Some(Point { x: 100, y: 200 });
|
||||||
|
|
||||||
match y {
|
// 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),
|
Some(p) => println!("Co-ordinates are {},{}", p.x, p.y),
|
||||||
_ => panic!("no match!"),
|
_ => panic!("No match!"),
|
||||||
}
|
}
|
||||||
y; // Fix without deleting this line.
|
|
||||||
|
println!("{optional_point:?}"); // Don't change this line.
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
// This function refuses to generate text to be printed on a nametag if you pass
|
// TODO: This function refuses to generate text to be printed on a nametag if
|
||||||
// it an empty string. It'd be nicer if it explained what the problem was,
|
// you pass it an empty string. It'd be nicer if it explained what the problem
|
||||||
// instead of just sometimes returning `None`. Thankfully, Rust has a similar
|
// was instead of just returning `None`. Thankfully, Rust has a similar
|
||||||
// construct to `Option` that can be used to express error conditions. Let's use
|
// construct to `Option` that can be used to express error conditions. Change
|
||||||
// it!
|
// the function signature and body to return `Result<String, String>` instead
|
||||||
|
// of `Option<String>`.
|
||||||
fn main() {
|
|
||||||
// You can optionally experiment here.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_nametag_text(name: String) -> Option<String> {
|
fn generate_nametag_text(name: String) -> Option<String> {
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
// Empty names aren't allowed.
|
// Empty names aren't allowed.
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(format!("Hi! My name is {}", name))
|
Some(format!("Hi! My name is {name}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -24,17 +24,18 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn generates_nametag_text_for_a_nonempty_name() {
|
fn generates_nametag_text_for_a_nonempty_name() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
generate_nametag_text("Beyoncé".into()),
|
generate_nametag_text("Beyoncé".to_string()).as_deref(),
|
||||||
Ok("Hi! My name is Beyoncé".into())
|
Ok("Hi! My name is Beyoncé"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn explains_why_generating_nametag_text_fails() {
|
fn explains_why_generating_nametag_text_fails() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
generate_nametag_text("".into()),
|
generate_nametag_text(String::new())
|
||||||
// Don't change this line
|
.as_ref()
|
||||||
Err("`name` was empty; it must be nonempty.".into())
|
.map_err(|e| e.as_str()),
|
||||||
|
Err("Empty names aren't allowed"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
// 5 tokens, and whenever you purchase items there is a processing fee of 1
|
// 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
|
// 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 `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
|
// the player typed in the quantity, we get it as a string. They might have
|
||||||
// might have typed anything, not just numbers!
|
// typed anything, not just numbers!
|
||||||
//
|
//
|
||||||
// Right now, this function isn't handling the error case at all (and isn't
|
// 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
|
// 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
|
// will return a `ParseIntError`. In that case, we want to immediately return
|
||||||
// return that error from our function and not try to multiply and add.
|
// 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!
|
// is a lot shorter!
|
||||||
|
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
@ -19,6 +19,8 @@ use std::num::ParseIntError;
|
||||||
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||||
let processing_fee = 1;
|
let processing_fee = 1;
|
||||||
let cost_per_item = 5;
|
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>();
|
||||||
|
|
||||||
Ok(qty * cost_per_item + processing_fee)
|
Ok(qty * cost_per_item + processing_fee)
|
||||||
|
@ -31,6 +33,7 @@ fn main() {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::num::IntErrorKind;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn item_quantity_is_a_valid_number() {
|
fn item_quantity_is_a_valid_number() {
|
||||||
|
@ -40,8 +43,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn item_quantity_is_an_invalid_number() {
|
fn item_quantity_is_an_invalid_number() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
total_cost("beep boop").unwrap_err().to_string(),
|
total_cost("beep boop").unwrap_err().kind(),
|
||||||
"invalid digit found in string"
|
&IntErrorKind::InvalidDigit,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,17 @@
|
||||||
|
|
||||||
use std::num::ParseIntError;
|
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() {
|
fn main() {
|
||||||
let mut tokens = 100;
|
let mut tokens = 100;
|
||||||
let pretend_user_input = "8";
|
let pretend_user_input = "8";
|
||||||
|
@ -14,14 +25,6 @@ fn main() {
|
||||||
println!("You can't afford that many!");
|
println!("You can't afford that many!");
|
||||||
} else {
|
} else {
|
||||||
tokens -= cost;
|
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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
struct PositiveNonzeroInteger(u64);
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
enum CreationError {
|
enum CreationError {
|
||||||
Negative,
|
Negative,
|
||||||
Zero,
|
Zero,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
impl PositiveNonzeroInteger {
|
impl PositiveNonzeroInteger {
|
||||||
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
fn new(value: i64) -> Result<Self, CreationError> {
|
||||||
// Hmm... Why is this always returning an Ok value?
|
// TODO: This function shouldn't always return an `Ok`.
|
||||||
Ok(PositiveNonzeroInteger(value as u64))
|
Ok(Self(value as u64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,11 +24,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_creation() {
|
fn test_creation() {
|
||||||
assert!(PositiveNonzeroInteger::new(10).is_ok());
|
|
||||||
assert_eq!(
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,18 @@
|
||||||
// This program uses an altered version of the code from errors4.
|
// 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
|
||||||
// This exercise uses some concepts that we won't get to until later in the
|
// `From` trait. It's not important to understand them in detail right now, but
|
||||||
// course, like `Box` and the `From` trait. It's not important to understand
|
// you can read ahead if you like. For now, think of the `Box<dyn ???>` type as
|
||||||
// them in detail right now, but you can read ahead if you like. For now, think
|
// an "I want anything that does ???" type.
|
||||||
// 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!
|
|
||||||
//
|
//
|
||||||
// In short, this particular use case for boxes is for when you want to own a
|
// 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
|
// 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
|
// trait. To do so, The `Box` is declared as of type `Box<dyn Trait>` where
|
||||||
// the trait the compiler looks for on any value used in that context. For this
|
// `Trait` is the trait the compiler looks for on any value used in that
|
||||||
// exercise, that context is the potential errors which can be returned in a
|
// context. For this exercise, that context is the potential errors which
|
||||||
// Result.
|
// 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?
|
|
||||||
|
|
||||||
use std::error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
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)]
|
#[derive(PartialEq, Debug)]
|
||||||
enum CreationError {
|
enum CreationError {
|
||||||
|
@ -40,17 +20,7 @@ enum CreationError {
|
||||||
Zero,
|
Zero,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PositiveNonzeroInteger {
|
// This is required so that `CreationError` can implement `Error`.
|
||||||
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`.
|
|
||||||
impl fmt::Display for CreationError {
|
impl fmt::Display for CreationError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let description = match *self {
|
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(())
|
||||||
|
}
|
||||||
|
|
|
@ -603,7 +603,7 @@ hint = """
|
||||||
Options can have a `Some` value, with an inner value, or a `None` value,
|
Options can have a `Some` value, with an inner value, or a `None` value,
|
||||||
without an inner 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
|
pattern match. Unwrapping is the easiest, but how do you do it safely so that
|
||||||
it doesn't panic in your face later?"""
|
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/if_let.html
|
||||||
- https://doc.rust-lang.org/rust-by-example/flow_control/while_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`
|
Also see `Option::flatten`
|
||||||
"""
|
"""
|
||||||
|
@ -631,7 +631,8 @@ hint = """
|
||||||
The compiler says a partial move happened in the `match` statement. How can
|
The compiler says a partial move happened in the `match` statement. How can
|
||||||
this be avoided? The compiler shows the correction needed.
|
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"""
|
https://doc.rust-lang.org/std/keyword.ref.html"""
|
||||||
|
|
||||||
# ERROR HANDLING
|
# 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:
|
To make this change, you'll need to:
|
||||||
- update the return type in the function signature to be a `Result<String,
|
- update the return type in the function signature to be a `Result<String,
|
||||||
String>` that could be the variants `Ok(String)` and `Err(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
|
- change the body of the function to return `Ok(…)` where it currently
|
||||||
returns `Some(stuff)`
|
returns `Some(…)`
|
||||||
- change the body of the function to return `Err(error message)` where it
|
- change the body of the function to return `Err(error message)` where it
|
||||||
currently returns `None`"""
|
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
|
`item_quantity.parse::<i32>()` where the cases are `Ok(something)` and
|
||||||
`Err(something)`.
|
`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!
|
does pretty much what you would make that match statement do for you!
|
||||||
|
|
||||||
Take a look at this section of the 'Error Handling' chapter:
|
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
|
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!"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "errors3"
|
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
|
common convention to return something like `Result<(), ErrorType>` from your
|
||||||
`main` function.
|
`main` function.
|
||||||
|
|
||||||
The unit (`()`) type is there because nothing is really needed in terms of
|
The unit type `()` is there because nothing is really needed in terms of a
|
||||||
positive results."""
|
positive result."""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "errors4"
|
name = "errors4"
|
||||||
dir = "13_error_handling"
|
dir = "13_error_handling"
|
||||||
hint = """
|
hint = """
|
||||||
`PositiveNonzeroInteger::new` is always creating a new instance and returning
|
`PositiveNonzeroInteger::new` is always creating a new instance and returning
|
||||||
an `Ok` result.
|
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
|
||||||
It should be doing some checking, returning an `Err` result if those checks
|
everything is… okay :)"""
|
||||||
fail, and only returning an `Ok` result if those checks determine that
|
|
||||||
everything is... okay :)"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "errors5"
|
name = "errors5"
|
||||||
dir = "13_error_handling"
|
dir = "13_error_handling"
|
||||||
test = false
|
test = false
|
||||||
hint = """
|
hint = """
|
||||||
There are two different possible `Result` types produced within `main()`, which
|
There are two different possible `Result` types produced within the `main`
|
||||||
are propagated using `?` operators. How do we declare a return type from
|
function, which are propagated using the `?` operators. How do we declare a
|
||||||
`main()` that allows both?
|
return type for the `main` function that allows both?
|
||||||
|
|
||||||
Under the hood, the `?` operator calls `From::from` on the error value to
|
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
|
convert it to a boxed trait object, a `Box<dyn Error>`. This boxed trait object
|
||||||
object is polymorphic, and since all errors implement the `error::Error` trait,
|
is polymorphic, and since all errors implement the `Error` trait, we can capture
|
||||||
we can capture lots of different errors in one "Box" object.
|
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
|
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:
|
Read more about boxing errors:
|
||||||
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
|
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:
|
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]]
|
[[exercises]]
|
||||||
name = "errors6"
|
name = "errors6"
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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:?}");
|
||||||
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue