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 = "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);
}