initial commit

This commit is contained in:
2025-02-19 10:34:15 +05:30
commit b9cb4c290a
355 changed files with 18626 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
[package]
name = "welcome_00"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,46 @@
// This is a Rust file. It is a plain text file with a `.rs` extension.
//
// Like most modern programming languages, Rust supports comments. You're looking at one right now!
// Comments are ignored by the compiler; you can leverage them to annotate code with notes and
// explanations.
// There are various ways to write comments in Rust, each with its own purpose.
// For now we'll stick to the most common one: the line comment.
// Everything from `//` to the end of the line is considered a comment.
// Exercises will include `TODO`, `todo!()` or `__` markers to draw your attention to the lines
// where you need to write code.
// You'll need to replace these markers with your own code to complete the exercise.
// Sometimes it'll be enough to write a single line of code, other times you'll have to write
// longer sections.
//
// If you get stuck for more than 10 minutes on an exercise, grab a trainer! We're here to help!
// You can also find solutions to all exercises in the `solutions` git branch.
fn greeting() -> &'static str {
// TODO: fix me 👇
"I'm ready to learn Rust!"
}
// Your solutions will be automatically verified by a set of tests.
// You can run these tests directly by invoking the `cargo test` command in your terminal,
// from the root of this exercise's directory. That's what the `wr` command does for you
// under the hood.
//
// Rust lets you write tests alongside your code.
// The `#[cfg(test)]` attribute tells the compiler to only compile the code below when
// running tests (i.e. when you run `cargo test`).
// You'll learn more about attributes and testing later in the course.
// For now, just know that you need to look for the `#[cfg(test)]` attribute to find the tests
// that will be verifying the correctness of your solutions!
//
// ⚠️ **DO NOT MODIFY THE TESTS** ⚠️
// They are there to help you validate your solutions. You should only change the code that's being
// tested, not the tests themselves.
#[cfg(test)]
mod tests {
use crate::greeting;
#[test]
fn test_welcome() {
assert_eq!(greeting(), "I'm ready to learn Rust!");
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "syntax"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,19 @@
// TODO: fix the function signature below to make the tests pass.
// Make sure to read the compiler error message—the Rust compiler is your pair programming
// 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: u32, b: u32) -> u32 {
// Don't touch the function body.
a + b * 2
}
#[cfg(test)]
mod tests {
use crate::compute;
#[test]
fn case() {
assert_eq!(compute(1, 2), 5);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "intro_01"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,14 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to build a calculator in Rust!");
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "integers"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,15 @@
fn compute(a: u32, b: u32) -> u32 {
// TODO: change the line below to fix the compiler error and make the tests pass.
let multiplier: u8 = 4;
a + b * multiplier
}
#[cfg(test)]
mod tests {
use crate::compute;
#[test]
fn case() {
assert_eq!(compute(1, 2), 9);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "variables"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,34 @@
// 👇 The lines below, starting with `///`, are called **documentation comments**.
// They attach documentation to the item that follows them. In this case, the `speed` function.
// If you run `cargo doc --open` from this exercise's directory, Rust will generate
// HTML documentation from these comments and open it in your browser.
/// Given the start and end points of a journey, and the time it took to complete it,
/// calculate the average speed.
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?
// Don't change the line below
distance / time_elapsed
}
#[cfg(test)]
mod tests {
use crate::speed;
#[test]
fn case1() {
assert_eq!(speed(0, 10, 10), 1);
}
#[test]
fn case2() {
assert_eq!(speed(10, 30, 10), 2);
}
#[test]
fn case3() {
assert_eq!(speed(10, 31, 10), 2);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "if_else"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,36 @@
/// Return `12` if `n` is even,
/// `13` if `n` is divisible by `3`,
/// `17` otherwise.
fn magic_number(n: u32) -> u32 {
todo!()
}
#[cfg(test)]
mod tests {
use crate::magic_number;
#[test]
fn one() {
assert_eq!(magic_number(1), 17);
}
#[test]
fn two() {
assert_eq!(magic_number(2), 12);
}
#[test]
fn six() {
assert_eq!(magic_number(6), 12);
}
#[test]
fn nine() {
assert_eq!(magic_number(9), 13);
}
#[test]
fn high() {
assert_eq!(magic_number(233), 17);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "panics"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,26 @@
/// Given the start and end points of a journey, and the time it took to complete the journey,
/// 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
(end - start) / time_elapsed
}
#[cfg(test)]
mod tests {
use crate::speed;
#[test]
fn case1() {
assert_eq!(speed(0, 10, 10), 1);
}
#[test]
// 👇 With the `#[should_panic]` annotation we can assert that we expect the code
// under test to panic. We can also check the panic message by using `expected`.
// This is all part of Rust's built-in test framework!
#[should_panic(expected = "The journey took no time at all. That's impossible!")]
fn by_zero() {
speed(0, 10, 0);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "factorial"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,36 @@
// Define a function named `factorial` that, given a non-negative integer `n`,
// returns `n!`, the factorial of `n`.
//
// The factorial of `n` is defined as the product of all positive integers up to `n`.
// For example, `5!` (read "five factorial") is `5 * 4 * 3 * 2 * 1`, which is `120`.
// `0!` is defined to be `1`.
//
// We expect `factorial(0)` to return `1`, `factorial(1)` to return `1`,
// `factorial(2)` to return `2`, and so on.
//
// Use only what you learned! No loops yet, so you'll have to use recursion!
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "while_"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,33 @@
// Rewrite the factorial function using a `while` loop.
pub fn factorial(n: u32) -> u32 {
// The `todo!()` macro is a placeholder that the compiler
// interprets as "I'll get back to this later", thus
// suppressing type errors.
// It panics at runtime.
todo!()
}
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "for_"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,29 @@
// Rewrite the factorial function using a `for` loop.
pub fn factorial(n: u32) -> u32 {
todo!()
}
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "overflow"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,49 @@
// Customize the `dev` profile to wrap around on overflow.
// Check Cargo's documentation to find out the right syntax:
// https://doc.rust-lang.org/cargo/reference/profiles.html
//
// For reasons that we'll explain later, the customization needs to be done in the `Cargo.toml`
// at the root of the repository, not in the `Cargo.toml` of the exercise.
pub fn factorial(n: u32) -> u32 {
let mut result = 1;
for i in 1..=n {
result *= i;
}
result
}
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn twentieth() {
// 20! is 2432902008176640000, which is too large to fit in a u32
// With the default dev profile, this will panic when you run `cargo test`
// We want it to wrap around instead
assert_eq!(factorial(20), 2_192_834_560);
// ☝️
// A large number literal using underscores to improve readability!
}
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "saturating"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,39 @@
pub fn factorial(n: u32) -> u32 {
let mut result = 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
}
#[cfg(test)]
mod tests {
use crate::factorial;
#[test]
fn twentieth() {
assert_eq!(factorial(20), u32::MAX);
}
#[test]
fn first() {
assert_eq!(factorial(0), 1);
}
#[test]
fn second() {
assert_eq!(factorial(1), 1);
}
#[test]
fn third() {
assert_eq!(factorial(2), 2);
}
#[test]
fn fifth() {
assert_eq!(factorial(5), 120);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "as_cast"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,37 @@
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct value after the conversion.
#[cfg(test)]
mod tests {
#[test]
fn u16_to_u32() {
let v: u32 = todo!();
assert_eq!(47u16 as u32, v);
}
#[test]
fn u8_to_i8() {
// The compiler is smart enough to know that the value 255 cannot fit
// inside an i8, so it'll emit a hard error. We intentionally disable
// this guardrail to make this (bad) conversion possible.
// The compiler is only able to pick on this because the value is a
// literal. If we were to use a variable, the compiler wouldn't be able to
// catch this at compile time.
#[allow(overflowing_literals)]
let x = { 255 as i8 };
// 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 to `u8`.
let y: i8 = todo!();
assert_eq!(x, y);
}
#[test]
fn bool_to_u8() {
let v: u8 = todo!();
assert_eq!(true as u8, v);
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "intro_02"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,14 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to start modelling a software ticket!");
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "struct_"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,29 @@
// Define a struct named `Order` with the following fields:
// - `price`, an unsigned integer
// - `quantity`, an unsigned integer
//
// It should also have a method named `is_available` that returns a `true` if the quantity is
// greater than 0, otherwise `false`.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_order_is_available() {
let order = Order {
price: 100,
quantity: 10,
};
assert!(order.is_available());
}
#[test]
fn test_order_is_not_available() {
let order = Order {
price: 100,
quantity: 0,
};
assert!(!order.is_available());
}
}

View File

@@ -0,0 +1,13 @@
[package]
name = "validation"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,74 @@
struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
// TODO: implement the `new` function.
// The following requirements should be met:
// - Only `To-Do`, `In Progress`, and `Done` statuses are allowed.
// - The `title` and `description` fields should not be empty.
// - the `title` should be at most 50 bytes long.
// - the `description` should be at most 500 bytes long.
// The method should panic if any of the requirements are not met.
// You can find the needed panic messages in the tests.
//
// You'll have to use what you learned in the previous exercises,
// 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!();
Self {
title,
description,
status,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
#[should_panic(expected = "Title cannot be empty")]
fn title_cannot_be_empty() {
Ticket::new("".into(), valid_description(), "To-Do".into());
}
#[test]
#[should_panic(expected = "Description cannot be empty")]
fn description_cannot_be_empty() {
Ticket::new(valid_title(), "".into(), "To-Do".into());
}
#[test]
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
fn title_cannot_be_longer_than_fifty_chars() {
Ticket::new(overly_long_title(), valid_description(), "To-Do".into());
}
#[test]
#[should_panic(expected = "Description cannot be longer than 500 bytes")]
fn description_cannot_be_longer_than_500_chars() {
Ticket::new(valid_title(), overly_long_description(), "To-Do".into());
}
#[test]
#[should_panic(expected = "Only `To-Do`, `In Progress`, and `Done` statuses are allowed")]
fn status_must_be_valid() {
Ticket::new(valid_title(), valid_description(), "Funny".into());
}
#[test]
fn done_is_allowed() {
Ticket::new(valid_title(), valid_description(), "Done".into());
}
#[test]
fn in_progress_is_allowed() {
Ticket::new(valid_title(), valid_description(), "In Progress".into());
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "modules"
version = "0.1.0"
edition = "2021"
[lints.rust]
# We silence dead code warnings for the time being in order to reduce
# compiler noise.
# We'll re-enable them again once we explain how visibility works in Rust.
dead_code = "allow"

View File

@@ -0,0 +1,40 @@
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.
fn create_todo_ticket(title: String, description: String) -> Ticket {
Ticket::new(title, description, "To-Do".into())
}
}
struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
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");
}
Ticket {
title,
description,
status,
}
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "visibility"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,77 @@
mod ticket {
struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
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");
}
Ticket {
title,
description,
status,
}
}
}
}
// TODO: **Exceptionally**, you'll be modifying both the `ticket` module and the `tests` module
// in this exercise.
#[cfg(test)]
mod tests {
// TODO: Add the necessary `pub` modifiers in the parent module to remove the compiler
// errors about the use statement below.
use super::ticket::Ticket;
// Be careful though! We don't want this function to compile after you have changed
// visibility to make the use statement compile!
// Once you have verified that it indeed doesn't compile, comment it out.
fn should_not_be_possible() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
// You should be seeing this error when trying to run this exercise:
//
// error[E0616]: field `description` of struct `Ticket` is private
// |
// | assert_eq!(ticket.description, "A description");
// | ^^^^^^^^^^^^^^^^^^
//
// 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");
}
fn encapsulation_cannot_be_violated() {
// This should be impossible as well, with a similar error as the one encountered above.
// (It will throw a compilation error only after you have commented the faulty line
// in the previous test - next compilation stage!)
//
// This proves that `Ticket::new` is now the only way to get a `Ticket` instance.
// It's impossible to create a ticket with an illegal title or description!
//
// 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(),
};
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "encapsulation"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,61 @@
pub mod ticket {
pub struct Ticket {
title: String,
description: String,
status: String,
}
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");
}
Ticket {
title,
description,
status,
}
}
// TODO: Add three public methods to the `Ticket` struct:
// - `title` that returns the `title` field.
// - `description` that returns the `description` field.
// - `status` that returns the `status` field.
}
}
#[cfg(test)]
mod tests {
use super::ticket::Ticket;
#[test]
fn description() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
assert_eq!(ticket.description(), "A description");
}
#[test]
fn title() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
assert_eq!(ticket.title(), "A title");
}
#[test]
fn status() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
assert_eq!(ticket.status(), "To-Do");
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "ownership"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,64 @@
// TODO: based on what we just learned about ownership, it sounds like immutable references
// are a good fit for our accessor methods.
// Change the existing implementation of `Ticket`'s accessor methods to take a reference
// to `self` as an argument, rather than taking ownership of it.
pub struct Ticket {
title: String,
description: String,
status: String,
}
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");
}
Ticket {
title,
description,
status,
}
}
pub fn title(self) -> String {
self.title
}
pub fn description(self) -> String {
self.description
}
pub fn status(self) -> String {
self.status
}
}
#[cfg(test)]
mod tests {
use super::Ticket;
#[test]
fn works() {
let ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
// If you change the signatures as requested, this should compile:
// we can call these methods one after the other because they borrow `self`
// rather than taking ownership of it.
assert_eq!(ticket.title(), "A title");
assert_eq!(ticket.description(), "A description");
assert_eq!(ticket.status(), "To-Do");
}
}

View File

@@ -0,0 +1,7 @@
[package]
name = "setters"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }

View File

@@ -0,0 +1,98 @@
// TODO: Add &mut-setters to the `Ticket` struct for each of its fields.
// Make sure to enforce the same validation rules you have in `Ticket::new`!
// Even better, extract that logic and reuse it in both places. You can use
// private functions or private static methods for that.
pub struct Ticket {
title: String,
description: String,
status: String,
}
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");
}
Ticket {
title,
description,
status,
}
}
pub fn title(&self) -> &String {
&self.title
}
pub fn description(&self) -> &String {
&self.description
}
pub fn status(&self) -> &String {
&self.status
}
}
#[cfg(test)]
mod tests {
use super::Ticket;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
fn works() {
let mut ticket = Ticket::new("A title".into(), "A description".into(), "To-Do".into());
ticket.set_title("A new title".into());
ticket.set_description("A new description".into());
ticket.set_status("Done".into());
assert_eq!(ticket.title(), "A new title");
assert_eq!(ticket.description(), "A new description");
assert_eq!(ticket.status(), "Done");
}
#[test]
#[should_panic(expected = "Title cannot be empty")]
fn title_cannot_be_empty() {
Ticket::new(valid_title(), valid_description(), "To-Do".into()).set_title("".into());
}
#[test]
#[should_panic(expected = "Description cannot be empty")]
fn description_cannot_be_empty() {
Ticket::new(valid_title(), valid_description(), "To-Do".into()).set_description("".into());
}
#[test]
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
fn title_cannot_be_longer_than_fifty_chars() {
Ticket::new(valid_title(), valid_description(), "To-Do".into())
.set_title(overly_long_title())
}
#[test]
#[should_panic(expected = "Description cannot be longer than 500 bytes")]
fn description_cannot_be_longer_than_500_chars() {
Ticket::new(valid_title(), valid_description(), "To-Do".into())
.set_description(overly_long_description())
}
#[test]
#[should_panic(expected = "Only `To-Do`, `In Progress`, and `Done` statuses are allowed")]
fn status_must_be_valid() {
Ticket::new(valid_title(), valid_description(), "To-Do".into()).set_status("Funny".into());
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "stack"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,21 @@
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct **stack size** for the respective type.
#[cfg(test)]
mod tests {
use std::mem::size_of;
#[test]
fn u16_size() {
assert_eq!(size_of::<u16>(), todo!());
}
#[test]
fn i32_size() {
assert_eq!(size_of::<i32>(), todo!());
}
#[test]
fn bool_size() {
assert_eq!(size_of::<bool>(), todo!());
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "heap"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,28 @@
pub struct Ticket {
title: String,
description: String,
status: String,
}
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct **stack size** for the respective type.
#[cfg(test)]
mod tests {
use super::Ticket;
use std::mem::size_of;
#[test]
fn string_size() {
assert_eq!(size_of::<String>(), todo!());
}
#[test]
fn ticket_size() {
// This is a tricky question!
// The "intuitive" answer happens to be the correct answer this time,
// but, in general, the memory layout of structs is a more complex topic.
// If you're curious, check out the "Type layout" section of The Rust Reference
// https://doc.rust-lang.org/reference/type-layout.html for more information.
assert_eq!(size_of::<Ticket>(), todo!());
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "references_in_memory"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,28 @@
pub struct Ticket {
title: String,
description: String,
status: String,
}
// TODO: based on what you learned in this section, replace `todo!()` with
// the correct **stack size** for the respective type.
#[cfg(test)]
mod tests {
use super::Ticket;
use std::mem::size_of;
#[test]
fn u16_ref_size() {
assert_eq!(size_of::<&u16>(), todo!());
}
#[test]
fn u64_mut_ref_size() {
assert_eq!(size_of::<&mut u64>(), todo!());
}
#[test]
fn ticket_ref_size() {
assert_eq!(size_of::<&Ticket>(), todo!());
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "destructor"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,16 @@
// We need some more machinery to write a proper exercise for destructors.
// 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 __!"
}
#[cfg(test)]
mod tests {
use crate::outro;
#[test]
fn test_outro() {
assert_eq!(outro(), "I have a basic understanding of destructors!");
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "outro_02"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,13 @@
// TODO: Define a new `Order` type.
// It should keep track of three pieces of information: `product_name`, `quantity`, and `unit_price`.
// The product name can't be empty and it can't be longer than 300 bytes.
// The quantity must be strictly greater than zero.
// The unit price is in cents and must be strictly greater than zero.
// Order must include a method named `total` that returns the total price of the order.
// Order must provide setters and getters for each field.
//
// Tests are located in a different place this time—in the `tests` folder.
// The `tests` folder is a special location for `cargo`. It's where it looks for **integration tests**.
// 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.

View File

@@ -0,0 +1,49 @@
use outro_02::Order;
// Files inside the `tests` directory are only compiled when you run tests.
// As a consequence, we don't need the `#[cfg(test)]` attribute for conditional compilation—it's
// implied.
#[test]
fn test_order() {
let mut order = Order::new("Rusty Book".to_string(), 3, 2999);
assert_eq!(order.product_name(), "Rusty Book");
assert_eq!(order.quantity(), &3);
assert_eq!(order.unit_price(), &2999);
assert_eq!(order.total(), 8997);
order.set_product_name("Rust Book".to_string());
order.set_quantity(2);
order.set_unit_price(3999);
assert_eq!(order.product_name(), "Rust Book");
assert_eq!(order.quantity(), &2);
assert_eq!(order.unit_price(), &3999);
assert_eq!(order.total(), 7998);
}
// Validation tests
#[test]
#[should_panic]
fn test_empty_product_name() {
Order::new("".to_string(), 3, 2999);
}
#[test]
#[should_panic]
fn test_long_product_name() {
Order::new("a".repeat(301), 3, 2999);
}
#[test]
#[should_panic]
fn test_zero_quantity() {
Order::new("Rust Book".to_string(), 0, 2999);
}
#[test]
#[should_panic]
fn test_zero_unit_price() {
Order::new("Rust Book".to_string(), 3, 0);
}

View File

@@ -0,0 +1,4 @@
[package]
name = "intro_03"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,14 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to learn about traits!");
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "trait_"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,23 @@
// Define a trait named `IsEven` that has a method `is_even` that returns a `true` if `self` is
// even, otherwise `false`.
//
// Then implement the trait for `u32` and `i32`.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_u32_is_even() {
assert!(42u32.is_even());
assert!(!43u32.is_even());
}
#[test]
fn test_i32_is_even() {
assert!(42i32.is_even());
assert!(!43i32.is_even());
assert!(0i32.is_even());
assert!(!(-1i32).is_even());
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "orphan"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,11 @@
// TODO: this is an example of an orphan rule violation.
// We're implementing a foreign trait (`PartialEq`, from `std`) on
// 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

@@ -0,0 +1,4 @@
[package]
name = "overloading"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,85 @@
use std::cmp::PartialEq;
struct Ticket {
title: String,
description: String,
status: String,
}
// TODO: Implement the `PartialEq` trait for `Ticket`.
impl PartialEq for Ticket {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partial_eq() {
let title = "title";
let description = "description";
let status = "To-Do";
let ticket1 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: status.to_string(),
};
assert!(ticket1 == ticket2);
}
#[test]
fn test_description_not_matching() {
let title = "title";
let status = "To-Do";
let ticket1 = Ticket {
title: title.to_string(),
description: "description".to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: "description2".to_string(),
status: status.to_string(),
};
assert!(ticket1 != ticket2);
}
#[test]
fn test_title_not_matching() {
let status = "To-Do";
let description = "description";
let ticket1 = Ticket {
title: "title".to_string(),
description: description.to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: "title2".to_string(),
description: description.to_string(),
status: status.to_string(),
};
assert!(ticket1 != ticket2);
}
#[test]
fn test_status_not_matching() {
let title = "title";
let description = "description";
let ticket1 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: "status".to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: "status2".to_string(),
};
assert!(ticket1 != ticket2);
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "derives"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,90 @@
// TODO: A (derivable) trait implementation is missing for this exercise to compile successfully.
// Fix it!
//
// # `Debug` primer
//
// `Debug` returns a representation of a Rust type that's suitable for debugging (hence the name).
// `assert_eq!` requires `Ticket` to implement `Debug` because, when the assertion fails, it tries to
// 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)]
struct Ticket {
title: String,
description: String,
status: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partial_eq() {
let title = "title";
let description = "description";
let status = "To-Do";
let ticket1 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: status.to_string(),
};
assert_eq!(ticket1, ticket2);
}
#[test]
fn test_description_not_matching() {
let title = "title";
let status = "To-Do";
let ticket1 = Ticket {
title: title.to_string(),
description: "description".to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: "description2".to_string(),
status: status.to_string(),
};
assert_ne!(ticket1, ticket2);
}
#[test]
fn test_title_not_matching() {
let status = "To-Do";
let description = "description";
let ticket1 = Ticket {
title: "title".to_string(),
description: description.to_string(),
status: status.to_string(),
};
let ticket2 = Ticket {
title: "title2".to_string(),
description: description.to_string(),
status: status.to_string(),
};
assert_ne!(ticket1, ticket2);
}
#[test]
fn test_status_not_matching() {
let title = "title";
let description = "description";
let ticket1 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: "status".to_string(),
};
let ticket2 = Ticket {
title: title.to_string(),
description: description.to_string(),
status: "status2".to_string(),
};
assert_ne!(ticket1, ticket2);
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "trait_bounds"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,15 @@
// TODO: Add the necessary trait bounds to `min` so that it compiles successfully.
// Refer to the documentation of the `std::cmp` module for more information on the traits you might need.
//
// Note: there are different trait bounds that'll make the compiler happy, but they come with
// different _semantics_. We'll cover those differences later in the course when we talk about ordered
// collections (e.g. BTreeMap).
/// Return the minimum of two values.
pub fn min<T>(left: T, right: T) -> T {
if left <= right {
left
} else {
right
}
}

View File

@@ -0,0 +1,7 @@
[package]
name = "str_slice"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }

View File

@@ -0,0 +1,61 @@
// TODO: Re-implement `Ticket`'s accessor methods. This time return a `&str` rather than a `&String`.
pub struct Ticket {
title: String,
description: String,
status: String,
}
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");
}
Ticket {
title,
description,
status,
}
}
pub fn title(&self) -> &String {
&self.title
}
pub fn description(&self) -> &String {
&self.description
}
pub fn status(&self) -> &String {
&self.status
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{valid_description, valid_title};
use std::any::{Any, TypeId};
#[test]
fn test_type() {
let ticket = Ticket::new(valid_title(), valid_description(), "To-Do".to_string());
// Some dark magic to verify that you used the expected return types
assert_eq!(TypeId::of::<str>(), ticket.title().type_id());
assert_eq!(TypeId::of::<str>(), ticket.description().type_id());
assert_eq!(TypeId::of::<str>(), ticket.status().type_id());
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "deref"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,38 @@
// TODO: whenever `title` and `description` are returned via their accessor methods, they
// should be normalized—i.e. leading and trailing whitespace should be removed.
// There is a method in Rust's standard library that can help with this, but you won't
// find it in the documentation for `String`.
// Can you figure out where it is defined and how to use it?
pub struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
pub fn title(&self) -> &str {
todo!()
}
pub fn description(&self) -> &str {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normalization() {
let ticket = Ticket {
title: " A title ".to_string(),
description: " A description ".to_string(),
status: "To-Do".to_string(),
};
assert_eq!("A title", ticket.title());
assert_eq!("A description", ticket.description());
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "sized"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,7 @@
pub fn example() {
// Trying to get the size of a str (or any other DST)
// via `std::mem::size_of` will result in a compile-time error.
//
// TODO: Comment out the following line and move on to the next exercise.
std::mem::size_of::<str>();
}

View File

@@ -0,0 +1,4 @@
[package]
name = "from"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,10 @@
// TODO: Implement the `From` trait for the `WrappingU32` type to make `example` compile.
pub struct WrappingU32 {
value: u32,
}
fn example() {
let wrapping: WrappingU32 = 42.into();
let wrapping = WrappingU32::from(42);
}

View File

@@ -0,0 +1,4 @@
[package]
name = "assoc_vs_generic"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,37 @@
// TODO: Define a new trait, `Power`, that has a method `power` that raises `self`
// to the power of `n`.
// The trait definition and its implementations should be enough to get
// the tests to compile and pass.
//
// Recommendation: you may be tempted to write a generic implementation to handle
// all cases at once. However, this is fairly complicated and requires the use of
// additional crates (i.e. `num-traits`).
// Even then, it might be preferable to use a simple macro instead to avoid
// the complexity of a highly generic implementation. Check out the
// "Little book of Rust macros" (https://veykril.github.io/tlborm/) if you're
// interested in learning more about it.
// You don't have to though: it's perfectly okay to write three separate
// implementations manually. Venture further only if you're curious.
#[cfg(test)]
mod tests {
use super::Power;
#[test]
fn test_power_u16() {
let x: u32 = 2_u32.power(3u16);
assert_eq!(x, 8);
}
#[test]
fn test_power_u32() {
let x: u32 = 2_u32.power(3u32);
assert_eq!(x, 8);
}
#[test]
fn test_power_ref_u32() {
let x: u32 = 2_u32.power(&3u32);
assert_eq!(x, 8);
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "clone"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,26 @@
// TODO: add the necessary `Clone` implementations (and invocations)
// to get the code to compile.
pub fn summary(ticket: Ticket) -> (Ticket, Summary) {
(ticket, ticket.summary())
}
pub struct Ticket {
pub title: String,
pub description: String,
pub status: String,
}
impl Ticket {
pub fn summary(self) -> Summary {
Summary {
title: self.title,
status: self.status,
}
}
}
pub struct Summary {
pub title: String,
pub status: String,
}

View File

@@ -0,0 +1,4 @@
[package]
name = "copy"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,25 @@
// TODO: implement the necessary traits to make the test compile and pass.
// You *can't* modify the test.
pub struct WrappingU32 {
value: u32,
}
impl WrappingU32 {
pub fn new(value: u32) -> Self {
Self { value }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ops() {
let x = WrappingU32::new(42);
let y = WrappingU32::new(31);
let z = WrappingU32::new(u32::MAX);
assert_eq!(x + y + y + z, WrappingU32::new(103));
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "drop"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,23 @@
// TODO: implement a so-called "Drop bomb": a type that panics when dropped
// unless a certain operation has been performed on it.
// You can see the expected API in the tests below.
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn test_drop_bomb() {
let bomb = DropBomb::new();
// The bomb should panic when dropped
}
#[test]
fn test_defused_drop_bomb() {
let mut bomb = DropBomb::new();
bomb.defuse();
// The bomb should not panic when dropped
// since it has been defused
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "outro_03"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,10 @@
// TODO: Define a new `SaturatingU16` type.
// It should hold a `u16` value.
// It should provide conversions from `u16`, `u8`, `&u16` and `&u8`.
// It should support addition with a right-hand side of type
// SaturatingU16, u16, &u16, and &SaturatingU16. Addition should saturate at the
// maximum value for `u16`.
// It should be possible to compare it with another `SaturatingU16` or a `u16`.
// It should be possible to print its debug representation.
//
// Tests are located in the `tests` folder—pay attention to the visibility of your types and methods.

View File

@@ -0,0 +1,17 @@
use outro_03::SaturatingU16;
#[test]
fn test_saturating_u16() {
let a: SaturatingU16 = (&10u8).into();
let b: SaturatingU16 = 5u8.into();
let c: SaturatingU16 = u16::MAX.into();
let d: SaturatingU16 = (&1u16).into();
let e = &c;
assert_eq!(a + b, SaturatingU16::from(15u16));
assert_eq!(a + c, SaturatingU16::from(u16::MAX));
assert_eq!(a + d, SaturatingU16::from(11u16));
assert_eq!(a + a, 20u16);
assert_eq!(a + 5u16, 15u16);
assert_eq!(a + e, SaturatingU16::from(u16::MAX));
}

View File

@@ -0,0 +1,4 @@
[package]
name = "intro_04"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,14 @@
fn intro() -> &'static str {
// TODO: fix me 👇
"I'm ready to __!"
}
#[cfg(test)]
mod tests {
use crate::intro;
#[test]
fn test_intro() {
assert_eq!(intro(), "I'm ready to refine the `Ticket` type!");
}
}

View File

@@ -0,0 +1,7 @@
[package]
name = "enum_"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }

View File

@@ -0,0 +1,127 @@
// TODO: use `Status` as type for `Ticket::status`
// Adjust the signature and implementation of all other methods as necessary.
#[derive(Debug, PartialEq)]
// `derive`s are recursive: it can only derive `PartialEq` if all fields also implement `PartialEq`.
// Same holds for `Debug`. Do what you must with `Status` to make this work.
struct Ticket {
title: String,
description: String,
status: String,
}
enum Status {
// TODO: add the missing variants
}
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");
}
Ticket {
title,
description,
status,
}
}
pub fn title(&self) -> &String {
&self.title
}
pub fn description(&self) -> &String {
&self.description
}
pub fn status(&self) -> &String {
&self.status
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{valid_description, valid_title};
#[test]
fn test_partial_eq() {
let title = valid_title();
let description = valid_description();
let ticket1 = Ticket {
title: title.clone(),
description: description.clone(),
status: Status::ToDo,
};
let ticket2 = Ticket {
title: title.clone(),
description: description.clone(),
status: Status::ToDo,
};
assert_eq!(ticket1, ticket2);
}
#[test]
fn test_description_not_matching() {
let title = valid_title();
let status = Status::ToDo;
let ticket1 = Ticket {
title: title.clone(),
description: "description".to_string(),
status,
};
let ticket2 = Ticket {
title: title.clone(),
description: "description2".to_string(),
status,
};
assert_ne!(ticket1, ticket2);
}
#[test]
fn test_title_not_matching() {
let description = valid_description();
let status = Status::InProgress;
let ticket1 = Ticket {
title: "title".to_string(),
description: description.clone(),
status,
};
let ticket2 = Ticket {
title: "title2".to_string(),
description: description.clone(),
status,
};
assert_ne!(ticket1, ticket2);
}
#[test]
fn test_status_not_matching() {
let title = valid_title();
let description = valid_description();
let ticket1 = Ticket {
title: title.clone(),
description: description.clone(),
status: Status::InProgress,
};
let ticket2 = Ticket {
title: title.clone(),
description: description.clone(),
status: Status::Done,
};
assert_ne!(ticket1, ticket2);
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "match_"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,44 @@
enum Shape {
Circle,
Square,
Rectangle,
Triangle,
Pentagon,
}
impl Shape {
// TODO: Implement the `n_sides` method using a `match`.
pub fn n_sides(&self) -> u8 {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_circle() {
assert_eq!(Shape::Circle.n_sides(), 0);
}
#[test]
fn test_square() {
assert_eq!(Shape::Square.n_sides(), 4);
}
#[test]
fn test_rectangle() {
assert_eq!(Shape::Rectangle.n_sides(), 4);
}
#[test]
fn test_triangle() {
assert_eq!(Shape::Triangle.n_sides(), 3);
}
#[test]
fn test_pentagon() {
assert_eq!(Shape::Pentagon.n_sides(), 5);
}
}

View File

@@ -0,0 +1,7 @@
[package]
name = "variants_with_data"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }

View File

@@ -0,0 +1,75 @@
// TODO: Implement `Ticket::assigned_to`.
// Return the name of the person assigned to the ticket, if the ticket is in progress.
// Panic otherwise.
#[derive(Debug, PartialEq)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> 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");
}
Ticket {
title,
description,
status,
}
}
pub fn assigned_to(&self) -> &str {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{valid_description, valid_title};
#[test]
#[should_panic(expected = "Only `In-Progress` tickets can be assigned to someone")]
fn test_todo() {
let ticket = Ticket::new(valid_title(), valid_description(), Status::ToDo);
ticket.assigned_to();
}
#[test]
#[should_panic(expected = "Only `In-Progress` tickets can be assigned to someone")]
fn test_done() {
let ticket = Ticket::new(valid_title(), valid_description(), Status::Done);
ticket.assigned_to();
}
#[test]
fn test_in_progress() {
let ticket = Ticket::new(
valid_title(),
valid_description(),
Status::InProgress {
assigned_to: "Alice".to_string(),
},
);
assert_eq!(ticket.assigned_to(), "Alice");
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "if_let"
version = "0.1.0"
edition = "2021"

View File

@@ -0,0 +1,39 @@
enum Shape {
Circle { radius: f64 },
Square { border: f64 },
Rectangle { width: f64, height: f64 },
}
impl Shape {
// TODO: Implement the `radius` method using
// either an `if let` or a `let/else`.
pub fn radius(&self) -> f64 {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_circle() {
let _ = Shape::Circle { radius: 1.0 }.radius();
}
#[test]
#[should_panic]
fn test_square() {
let _ = Shape::Square { border: 1.0 }.radius();
}
#[test]
#[should_panic]
fn test_rectangle() {
let _ = Shape::Rectangle {
width: 1.0,
height: 2.0,
}
.radius();
}
}

View File

@@ -0,0 +1,7 @@
[package]
name = "nullability"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }

View File

@@ -0,0 +1,71 @@
// TODO: Implement `Ticket::assigned_to` using `Option` as the return type.
#[derive(Debug, PartialEq)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> 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");
}
Ticket {
title,
description,
status,
}
}
pub fn assigned_to(&self) -> Option<&String> {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{valid_description, valid_title};
#[test]
fn test_todo() {
let ticket = Ticket::new(valid_title(), valid_description(), Status::ToDo);
assert!(ticket.assigned_to().is_none());
}
#[test]
fn test_done() {
let ticket = Ticket::new(valid_title(), valid_description(), Status::Done);
assert!(ticket.assigned_to().is_none());
}
#[test]
fn test_in_progress() {
let ticket = Ticket::new(
valid_title(),
valid_description(),
Status::InProgress {
assigned_to: "Alice".to_string(),
},
);
assert_eq!(ticket.assigned_to(), Some(&"Alice".to_string()));
}
}

View File

@@ -0,0 +1,7 @@
[package]
name = "fallibility"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }

View File

@@ -0,0 +1,71 @@
// TODO: Convert the `Ticket::new` method to return a `Result` instead of panicking.
// Use `String` as the error type.
#[derive(Debug, PartialEq)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> 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");
}
Ticket {
title,
description,
status,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
fn title_cannot_be_empty() {
let error = Ticket::new("".into(), valid_description(), Status::ToDo).unwrap_err();
assert_eq!(error, "Title cannot be empty");
}
#[test]
fn description_cannot_be_empty() {
let error = Ticket::new(valid_title(), "".into(), Status::ToDo).unwrap_err();
assert_eq!(error, "Description cannot be empty");
}
#[test]
fn title_cannot_be_longer_than_fifty_chars() {
let error =
Ticket::new(overly_long_title(), valid_description(), Status::ToDo).unwrap_err();
assert_eq!(error, "Title cannot be longer than 50 bytes");
}
#[test]
fn description_cannot_be_longer_than_500_chars() {
let error =
Ticket::new(valid_title(), overly_long_description(), Status::ToDo).unwrap_err();
assert_eq!(error, "Description cannot be longer than 500 bytes");
}
}

View File

@@ -0,0 +1,7 @@
[package]
name = "unwrap"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
common = { path = "../../../helpers/common" }

View File

@@ -0,0 +1,73 @@
// TODO: `easy_ticket` should panic when the title is invalid.
// When the description is invalid, instead, it should use a default description:
// "Description not provided".
fn easy_ticket(title: String, description: String, status: Status) -> Ticket {
todo!()
}
#[derive(Debug, PartialEq, Clone)]
struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq, Clone)]
enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> Result<Ticket, String> {
if title.is_empty() {
return Err("Title cannot be empty".to_string());
}
if title.len() > 50 {
return Err("Title cannot be longer than 50 bytes".to_string());
}
if description.is_empty() {
return Err("Description cannot be empty".to_string());
}
if description.len() > 500 {
return Err("Description cannot be longer than 500 bytes".to_string());
}
Ok(Ticket {
title,
description,
status,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::{overly_long_description, overly_long_title, valid_description, valid_title};
#[test]
#[should_panic(expected = "Title cannot be empty")]
fn title_cannot_be_empty() {
easy_ticket("".into(), valid_description(), Status::ToDo);
}
#[test]
fn template_description_is_used_if_empty() {
let ticket = easy_ticket(valid_title(), "".into(), Status::ToDo);
assert_eq!(ticket.description, "Description not provided");
}
#[test]
#[should_panic(expected = "Title cannot be longer than 50 bytes")]
fn title_cannot_be_longer_than_fifty_chars() {
easy_ticket(overly_long_title(), valid_description(), Status::ToDo);
}
#[test]
fn template_description_is_used_if_too_long() {
let ticket = easy_ticket(valid_title(), overly_long_description(), Status::ToDo);
assert_eq!(ticket.description, "Description not provided");
}
}

Some files were not shown because too many files have changed in this diff Show More