Compare commits

...

31 Commits

Author SHA1 Message Date
7a1ca00837 solution to exercise 04_05 2024-07-13 17:39:46 +02:00
1789d43730 solution to exercise 04_04 2024-07-13 17:30:01 +02:00
604a5a20ae solution to exercise 04_03 2024-07-01 16:30:43 +02:00
0829174983 solution to exercise 04_02 2024-07-01 16:23:02 +02:00
ebe07acd1e solution to exercise 04_01 2024-07-01 16:15:42 +02:00
4a4fc2ea0d solution to exercise 03_12 2024-06-27 16:38:30 +02:00
91e6bf5ae4 solution to exercise 03_11 2024-06-27 16:29:31 +02:00
3f57819e4c solution to exercise 03_10 2024-06-27 16:24:32 +02:00
d54a1e160b solution to exercise 03_09 2024-06-27 16:16:19 +02:00
4913da035f solution to exercise 03_08 2024-06-27 16:08:42 +02:00
1f0823a6a4 solution to exercise 03_07 2024-06-23 18:22:03 +02:00
56075fa40e solution to exercise 03_06 2024-06-23 18:08:59 +02:00
852cd5f0b9 solution to exercise 03_05 2024-06-23 17:58:03 +02:00
1c16ac8c22 solution to exercise 03_04 2024-06-23 17:54:39 +02:00
d3b4c0d653 solution to exercise 03_03 2024-06-19 15:03:51 +02:00
8b78ea3a83 solution to exercise 03_02 2024-06-19 14:56:36 +02:00
0e5cdc6d10 solution to exercise 03_01 2024-06-19 14:46:00 +02:00
1d876eff10 solution to exercise 03_00 2024-06-19 14:41:41 +02:00
dfcb9736b2 solution to exercise 02_10 2024-06-17 11:31:06 +02:00
c1e7c3fadd solution to exercise 02_09 2024-06-17 11:22:25 +02:00
978a99b00d solution to exercise 02_08 2024-06-17 11:15:21 +02:00
34189d212e solution to exercise 02_07 2024-06-17 10:59:03 +02:00
9d1869fc9c solution to exercise 02_06 2024-06-17 10:53:22 +02:00
3e56cf1287 solution to exercise 02_05 2024-06-15 21:01:03 +02:00
f91cc0089a solution to exercise 02_04 2024-06-13 17:04:21 +02:00
211f23cea4 solution to exercise 02_03 2024-06-13 16:55:06 +02:00
1e516bf0a5 solution to exercise 02_02 2024-06-13 16:46:44 +02:00
03a0a77394 solution to exercise 02_01 2024-06-13 16:40:40 +02:00
bb27bfad41 solution to exercise 02_00 2024-06-13 16:33:07 +02:00
4d12facc2f solution to exercise 01 2024-06-12 17:12:56 +02:00
1251a6c1b1 solution to exercise 00 2024-06-12 17:06:43 +02:00
32 changed files with 241 additions and 65 deletions

View File

@ -1,3 +1,6 @@
[workspace]
members = ["exercises/*/*", "helpers/common", "helpers/mdbook-exercise-linker", "helpers/ticket_fields"]
resolver = "2"
[profile.dev]
overflow-checks = false

View File

@ -17,7 +17,7 @@
// You can also find solutions to all exercises in the `solutions` git branch.
fn greeting() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
"I'm ready to learn Rust!"
}
// Your solutions will be automatically verified by a set of tests.

View File

@ -3,7 +3,7 @@
// partner in this course and it'll often guide you in the right direction!
//
// The input parameters should have the same type of the return type.
fn compute(a, b) -> u32 {
fn compute(a: u32, b: u32) -> u32 {
// Don't touch the function body.
a + b * 2
}
@ -16,4 +16,4 @@ mod tests {
fn case() {
assert_eq!(compute(1, 2), 5);
}
}
}

View File

@ -1,6 +1,6 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
"I'm ready to build a calculator in Rust!"
}
#[cfg(test)]

View File

@ -1,6 +1,6 @@
fn compute(a: u32, b: u32) -> u32 {
// TODO: change the line below to fix the compiler error and make the tests pass.
a + b * 4u8
a + b * 4u32
}
#[cfg(test)]

View File

@ -8,7 +8,7 @@
pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: define a variable named `distance` with the right value to get tests to pass
// Do you need to annotate the type of `distance`? Why or why not?
let distance: u32 = end - start;
// Don't change the line below
distance / time_elapsed
}

View File

@ -1,6 +1,6 @@
/// Return `true` if `n` is even, `false` otherwise.
fn is_even(n: u32) -> bool {
todo!()
n % 2 == 0
}
#[cfg(test)]

View File

@ -2,7 +2,9 @@
/// calculate the average speed of the journey.
fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: Panic with a custom message if `time_elapsed` is 0
if time_elapsed == 0 {
panic!("The journey took no time at all, that's impossible!");
}
(end - start) / time_elapsed
}

View File

@ -9,6 +9,13 @@
// `factorial(2)` to return `2`, and so on.
//
// Use only what you learned! No loops yet, so you'll have to use recursion!
fn factorial(n: u16) -> u16 {
if n == 0 {
1
} else {
n * factorial(n - 1)
}
}
#[cfg(test)]
mod tests {

View File

@ -4,7 +4,13 @@ pub fn factorial(n: u32) -> u32 {
// interprets as "I'll get back to this later", thus
// suppressing type errors.
// It panics at runtime.
todo!()
let mut result: u32 = 1; // base case
let mut i: u32 = 1;
while i <= n {
result *= i;
i += 1;
}
result
}
#[cfg(test)]

View File

@ -1,6 +1,10 @@
// Rewrite the factorial function using a `for` loop.
pub fn factorial(n: u32) -> u32 {
todo!()
let mut result: u32 = 1; // base case
for i in 2..=n {
result *= i;
}
result
}
#[cfg(test)]

View File

@ -1,9 +1,9 @@
pub fn factorial(n: u32) -> u32 {
let mut result = 1;
let mut result: u32 = 1;
for i in 1..=n {
// Use saturating multiplication to stop at the maximum value of u32
// rather than overflowing and wrapping around
result *= i;
result = result.saturating_mul(i);
}
result
}

View File

@ -6,7 +6,7 @@ mod tests {
#[test]
fn u16_to_u32() {
let v: u32 = todo!();
let v: u32 = 47;
assert_eq!(47u16 as u32, v);
}
@ -24,14 +24,14 @@ mod tests {
// You could solve this by using exactly the same expression as above,
// but that would defeat the purpose of the exercise. Instead, use a genuine
// `i8` value that is equivalent to `255` when converted from `u8`.
let y: i8 = todo!();
let y: i8 = -1;
assert_eq!(x, y);
}
#[test]
fn bool_to_u8() {
let v: u8 = todo!();
let v: u8 = 1;
assert_eq!(true as u8, v);
}
}

View File

@ -1,6 +1,6 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
"I'm ready to start modelling a software ticket!"
}
#[cfg(test)]

View File

@ -4,6 +4,16 @@
//
// It should also have a method named `is_available` that returns a `true` if the quantity is
// greater than 0, otherwise `false`.
struct Order {
price: u8,
quantity: u8,
}
impl Order {
fn is_available(self) -> bool {
self.quantity > 0
}
}
#[cfg(test)]
mod tests {

View File

@ -17,7 +17,22 @@ impl Ticket {
// as well as some `String` methods. Use the documentation of Rust's standard library
// to find the most appropriate options -> https://doc.rust-lang.org/std/string/struct.String.html
fn new(title: String, description: String, status: String) -> Self {
todo!();
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed")
}
if title.is_empty() {
panic!("Title cannot be empty")
}
if description.is_empty() {
panic!("Description cannot be empty")
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes")
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes")
}
Self {
title,
description,

View File

@ -1,6 +1,7 @@
mod helpers {
// TODO: Make this code compile, either by adding a `use` statement or by using
// the appropriate path to refer to the `Ticket` struct.
use super::Ticket;
fn create_todo_ticket(title: String, description: String) -> Ticket {
Ticket::new(title, description, "To-Do".into())

View File

@ -1,12 +1,12 @@
mod ticket {
struct Ticket {
pub struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
fn new(title: String, description: String, status: String) -> Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
@ -55,7 +55,7 @@ mod tests {
//
// TODO: Once you have verified that the below does not compile,
// comment the line out to move on to the next exercise!
assert_eq!(ticket.description, "A description");
// assert_eq!(ticket.description, "A description");
}
fn encapsulation_cannot_be_violated() {
@ -68,10 +68,10 @@ mod tests {
//
// TODO: Once you have verified that the below does not compile,
// comment the lines out to move on to the next exercise!
let ticket = Ticket {
title: "A title".into(),
description: "A description".into(),
status: "To-Do".into(),
};
// let ticket = Ticket {
// title: "A title".into(),
// description: "A description".into(),
// status: "To-Do".into(),
// };
}
}

View File

@ -34,6 +34,17 @@ pub mod ticket {
// - `title` that returns the `title` field.
// - `description` that returns the `description` field.
// - `status` that returns the `status` field.
pub fn title(self) -> String {
self.title
}
pub fn description(self) -> String {
self.description
}
pub fn status(self) -> String {
self.status
}
}
}

View File

@ -34,16 +34,16 @@ impl Ticket {
}
}
pub fn title(self) -> String {
self.title
pub fn title(&self) -> &String {
&self.title
}
pub fn description(self) -> String {
self.description
pub fn description(&self) -> &String {
&self.description
}
pub fn status(self) -> String {
self.status
pub fn status(&self) -> &String {
&self.status
}
}

View File

@ -11,21 +11,9 @@ pub struct Ticket {
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
validate_title(&title);
validate_description(&description);
validate_status(&status);
Ticket {
title,
@ -45,6 +33,45 @@ impl Ticket {
pub fn status(&self) -> &String {
&self.status
}
pub fn set_title(&mut self, title: String) {
validate_title(&title);
self.title = title;
}
pub fn set_description(&mut self, description: String) {
validate_description(&description);
self.description = description;
}
pub fn set_status(&mut self, status: String) {
validate_status(&status);
self.status = status;
}
}
fn validate_title(title: &String) {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
}
fn validate_description(description: &String) {
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
}
fn validate_status(status: &String) {
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
}
#[cfg(test)]

View File

@ -6,16 +6,16 @@ mod tests {
#[test]
fn u16_size() {
assert_eq!(size_of::<u16>(), todo!());
assert_eq!(size_of::<u16>(), 2);
}
#[test]
fn i32_size() {
assert_eq!(size_of::<i32>(), todo!());
assert_eq!(size_of::<i32>(), 4);
}
#[test]
fn bool_size() {
assert_eq!(size_of::<bool>(), todo!());
assert_eq!(size_of::<bool>(), 1);
}
}

View File

@ -13,7 +13,7 @@ mod tests {
#[test]
fn string_size() {
assert_eq!(size_of::<String>(), todo!());
assert_eq!(size_of::<String>(), 24);
}
#[test]
@ -23,6 +23,6 @@ mod tests {
// but, in general, the memory layout of structs is a more complex topic.
// If you're curious, check out the "Data layout" section of the Rustonomicon
// https://doc.rust-lang.org/nomicon/data.html for more information.
assert_eq!(size_of::<Ticket>(), todo!());
assert_eq!(size_of::<Ticket>(), 72);
}
}

View File

@ -13,16 +13,16 @@ mod tests {
#[test]
fn u16_ref_size() {
assert_eq!(size_of::<&u16>(), todo!());
assert_eq!(size_of::<&u16>(), 8);
}
#[test]
fn u64_mut_ref_size() {
assert_eq!(size_of::<&mut u64>(), todo!());
assert_eq!(size_of::<&mut u64>(), 8);
}
#[test]
fn ticket_ref_size() {
assert_eq!(size_of::<&Ticket>(), todo!());
assert_eq!(size_of::<&Ticket>(), 8);
}
}

View File

@ -2,7 +2,7 @@
// We'll pick the concept up again in a later chapter after covering traits and
// interior mutability.
fn outro() -> &'static str {
"I have a basic understanding of __!"
"I have a basic understanding of destructors!"
}
#[cfg(test)]

View File

@ -11,3 +11,74 @@
// Integration here has a very specific meaning: they test **the public API** of your project.
// You'll need to pay attention to the visibility of your types and methods; integration
// tests can't access private or `pub(crate)` items.
pub struct Order {
product_name: String,
quantity: u32,
unit_price: u32,
}
impl Order {
pub fn new(product_name: String, quantity: u32, unit_price: u32) -> Order {
validate_product_name(&product_name);
validate_quantity(&quantity);
validate_unit_price(&unit_price);
Order {
product_name,
quantity,
unit_price,
}
}
pub fn product_name(&self) -> &String {
&self.product_name
}
pub fn quantity(&self) -> &u32 {
&self.quantity
}
pub fn unit_price(&self) -> &u32 {
&self.unit_price
}
pub fn set_product_name(&mut self, product_name: String) {
validate_product_name(&product_name);
self.product_name = product_name;
}
pub fn set_quantity(&mut self, quantity: u32) {
validate_quantity(&quantity);
self.quantity = quantity;
}
pub fn set_unit_price(&mut self, unit_price: u32) {
validate_unit_price(&unit_price);
self.unit_price = unit_price;
}
pub fn total(&self) -> u32 {
self.quantity * self.unit_price
}
}
fn validate_product_name(product_name: &String) {
if product_name.is_empty() {
panic!("Product name cannot be empty");
}
if product_name.len() > 300 {
panic!("Product name cannot be longer than 300 bytes");
}
}
fn validate_quantity(quantity: &u32) {
if quantity == &0 {
panic!("Quantity must be greater than zero");
}
}
fn validate_unit_price(unit_price: &u32) {
if unit_price == &0 {
panic!("Unit price must be greater than zero");
}
}

View File

@ -1,6 +1,6 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
"I'm ready to learn about traits!"
}
#[cfg(test)]

View File

@ -3,6 +3,22 @@
//
// Then implement the trait for `u32` and `i32`.
pub trait IsEven {
fn is_even(self) -> bool;
}
impl IsEven for u32 {
fn is_even(self) -> bool {
self % 2 == 0
}
}
impl IsEven for i32 {
fn is_even(self) -> bool {
self % 2 == 0
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -3,9 +3,3 @@
// a foreign type (`u32`, from `std`).
// Look at the compiler error to get familiar with what it looks like.
// Then delete the code below and move on to the next exercise.
impl PartialEq for u32 {
fn eq(&self, _other: &Self) -> bool {
todo!()
}
}

View File

@ -8,7 +8,13 @@ struct Ticket {
// TODO: Implement the `PartialEq` trait for `Ticket`.
impl PartialEq for Ticket {}
impl PartialEq for Ticket {
fn eq(&self, other: &Self) -> bool {
self.title == other.title
&& self.description == other.description
&& self.status == other.status
}
}
#[cfg(test)]
mod tests {

View File

@ -8,7 +8,7 @@
// print both sides of the comparison to the terminal.
// If the compared type doesn't implement `Debug`, it doesn't know how to represent them!
#[derive(PartialEq)]
#[derive(Debug, PartialEq)]
struct Ticket {
title: String,
description: String,

View File

@ -6,7 +6,10 @@
// collections (e.g. BTreeMap).
/// Return the minimum of two values.
pub fn min<T>(left: T, right: T) -> T {
pub fn min<T>(left: T, right: T) -> T
where
T: Ord,
{
if left <= right {
left
} else {