diff --git a/README.md b/README.md index eb64b41..b254135 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,4 @@ ![Rust workflow](https://github.com/shahaba/rust-projects/actions/workflows/rust.yml/badge.svg) [![Build status](https://badge.buildkite.com/679c89fce737457b4c095d3f2888701c89bcdb3bb41dfcf502.svg)](https://buildkite.com/personal-192/rust-project-builder) [![codecov](https://codecov.io/gh/shahaba/rust-projects/graph/badge.svg?token=CQ1RTWRTC6)](https://codecov.io/gh/shahaba/rust-projects) -## Rust Book Readings -- [CH 2 - Guessing Game](rust-book/ch02-guessing-game/README.md) -- [CH 3 - Variables](rust-book/ch-03-variables/README.md) -- [CH 4 - Ownership](rust-book/ch04-ownership/README.md) -- [CH 5 - Structs](rust-book/ch05-structs/README.md) -- [CH 6 - Enums](rust-book/ch06-enums/README.md) -- [CH 7 - Packages Project](rust-book/ch07-packages/README.md) -- [CH 8 - Collections](rust-book/ch08-collections/README.md) -- [CH 9 - Error Handling](rust-book/ch09-error_handling/README.md) -- [CH 10 - Generics](rust-book/ch10-generics/README.md) -- [CH 11 - Automated Tests](rust-book/ch11-automated-tests/README.md) -- [CH 12 - IO Project](rust-book/ch12-minigrep/README.md) -- [CH 13 - Functional Language Features](rust-book/ch13-closures/README.md) -- CH 14 - Cargo & Crates (Skipped) -- [CH 15 - Smart Pointers](rust-book/ch15-smart-pointers/README.md) - -My working repo for going through the [Rust Book](https://rust-book.cs.brown.edu/title-page.html) - -You can go into any of the folders and run: `cargo run` to compile and run the rust program (if you have `rustc` installed) - -## Basic Rust Algorithms - -Projects around implementing basic algorithms in Rust diff --git a/rust-book/ch02-guessing-game/Cargo.toml b/rust-book/ch02-guessing-game/Cargo.toml deleted file mode 100644 index ee57aa5..0000000 --- a/rust-book/ch02-guessing-game/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "guessing_game" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -rand = "^0.8" \ No newline at end of file diff --git a/rust-book/ch02-guessing-game/README.md b/rust-book/ch02-guessing-game/README.md deleted file mode 100644 index 48168de..0000000 --- a/rust-book/ch02-guessing-game/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Chapter 2 - Guessing Game - -A quick dive into some of the core concepts in Rust like creating variables, loops, and accepting input from the command line (aka the user). diff --git a/rust-book/ch02-guessing-game/src/main.rs b/rust-book/ch02-guessing-game/src/main.rs deleted file mode 100644 index 3ed9732..0000000 --- a/rust-book/ch02-guessing-game/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::{cmp::Ordering, io}; -use rand::Rng; - -fn main() { - println!("Welcome, to the guessing game"); - - let secret_number = rand::thread_rng().gen_range(1..=100); - - println!("The secret number is: {secret_number}"); - - loop { - println!("Please enter a number"); - - let mut guess = String::new(); - - io::stdin() - .read_line(&mut guess) - .expect("Failed to read line"); - - let guess: u32 = match guess.trim().parse() { - Ok(num) => num, - Err(_) => continue, - }; - - println!("Your guess: {guess}"); - - match guess.cmp(&secret_number) { - Ordering::Less => println!("Too small!"), - Ordering::Equal => { - println!("You win!"); - break; - }, - Ordering::Greater => println!("Too big!") - } - } -} diff --git a/rust-book/ch03-variables/Cargo.toml b/rust-book/ch03-variables/Cargo.toml deleted file mode 100644 index f1cad76..0000000 --- a/rust-book/ch03-variables/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "variables" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch03-variables/README.md b/rust-book/ch03-variables/README.md deleted file mode 100644 index ee2daeb..0000000 --- a/rust-book/ch03-variables/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Chapter 3 - Variables and Controls - -Exploration of variable assignment, mutability, and other features like Shadowing that give a baseline understand of how to write simple programs with Rust. Also introduces function and control flow syntax: - -## Control Flow - -### `if` and `else if` - -A conditional expression that performs different actions depending on the value of a statement - -Ex. - -```Rust -let condition: bool = false; - -if condition { - println!("Hello World"); -} else { - println!("Nevermind"); -} -``` - -**Note:** Unlike other languages, Rust won't convert non-booleans (like int: 0, 1) into booleans automatically. We need to explicitly pass a boolean to a `if` expression - -### `loop`, `while`, and `for` - -- `loop` - - Allows us to execute a block of code indefinitely, or until we explicitly tell it to stop -- `while` - - Allows us to run a `loop`, while a given a condition is `true` -- `for` - - Allows us to `loop` through a collection of elements diff --git a/rust-book/ch03-variables/src/main.rs b/rust-book/ch03-variables/src/main.rs deleted file mode 100644 index 8777dbd..0000000 --- a/rust-book/ch03-variables/src/main.rs +++ /dev/null @@ -1,116 +0,0 @@ - -const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3; - -fn main() { - variables(); - another_function(5); - println!("Expression: {}", expression()); - branches(); -} - -fn variables() { - println!("3hrs in seconds: {THREE_HOURS_IN_SECONDS}"); - - let mut x:u8 = 5; - println!("The value of x is: {x}"); - - x = 6; - println!("The value of x is: {x}"); - - { - let x = x * 2; - println!("The value of x is: {x}"); - } - - println!("The value of x is: {x}"); - - // floating point number - let x: f32 = 3.0; - println!("The value of x is: {x}"); - - // tuples - let tup: (i32, f64, u8) = (500, 6.4, 5); - - let var = tup.0; - println!("The first value of tup is: {var}"); - - // destructuring - let (x, y, z) = tup; - println!("The first value of tup is: {x}"); - println!("The second value of tup is: {y}"); - println!("The third value of tup is: {z}"); - - // arrays - let a: [i32; 5] = [1, 2, 3, 4, 5]; - println!("{}", a[0]); -} - -fn another_function(x: i32) { - println!("Another message, the value of x is: {x}") -} - -fn expression() -> i32 { - let y = 5; - return y + 1 -} - -fn branches() { - let number = 5; - - if number < 5 { - println!("condition was true") - } else if number > 5 && number < 10 { - println!("condition was false") - } - - let condition = true; - let number = if condition { 5 } else { 6 }; - println!("Number was: {number}"); - - // a casual infinite loop without break - loop { - println!("again!"); - break println!("Get me out of here"); - } - - let mut counter: i8 = 0; - let result = loop { - counter += 1; - - if counter == 10 { - break counter * 2; - } - }; - - println!("The result was: {result}"); - - let mut count = 0; - 'counting_up: loop { - println!("count = {count}"); - let mut remaining = 10; - - loop { - println!("remaining = {remaining}"); - if remaining == 9 { - break; - } - if count == 2 { - break 'counting_up; - } - remaining -= 1; - } - - count += 1; - } - println!("End count = {count}"); - - let mut number = 3; - - while number != 0 { - println!("{number}!"); - - number -= 1; - } - - println!("LIFTOFF!!!"); -} \ No newline at end of file diff --git a/rust-book/ch04-ownership/Cargo.toml b/rust-book/ch04-ownership/Cargo.toml deleted file mode 100644 index e884752..0000000 --- a/rust-book/ch04-ownership/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "ownership" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/rust-book/ch04-ownership/README.md b/rust-book/ch04-ownership/README.md deleted file mode 100644 index 344385c..0000000 --- a/rust-book/ch04-ownership/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Chapter 4 - Ownership - -One of Rust's core concept to ensure programs run and use memory **safely**. Ownership is divided into three main principles: owning, referencing, and lifetimes. - -1. Owning - Every value can only have a single owner at any given time. - 1. This deters race conditions that might allow for multiple alias of a variable to concurrently mutate the data. - 2. Ownership can be moved between variables, but there is always a single clear owner of the data. -2. Referencing (or Borrowing) - A **non-owning pointer** to data. - 1. Immutable (or Shared) Reference - created via `&` operator, allows us read access to data reference by pointer. - 2. Mutable (or Unique) Reference - created via `&mut` operator, allows us to mutate data referenced by pointer. -3. Lifetimes - Ensures the data that is being borrowed outlives its references. - 1. More in Chapter 10 diff --git a/rust-book/ch04-ownership/src/main.rs b/rust-book/ch04-ownership/src/main.rs deleted file mode 100644 index cbafe8e..0000000 --- a/rust-book/ch04-ownership/src/main.rs +++ /dev/null @@ -1,75 +0,0 @@ -fn main() { - // Create a heap for array [0; 10] - let a = Box::new([0; 10]); - println!("a: {:?}", a); - // Change ownership of pointer to array from a --> b - let b = a; - println!("New owner b: {:?}", b); - - // Managing ownership of `first` - let first = String::from("Micheal"); - let first_clone = first.clone(); - - // Mutate first, creating a new reference to string - let first_suffix = add_suffix(first); - println!("{first_suffix} - Originally {first_clone}"); - - // What about passing arguments but not losing the ownership? - // This is where references - let m1: String = String::from("hello"); - let m2: String = String::from("world"); - reference(&m1, &m2); - - println!("{m1} {m2}"); - - let mut x: i32 = 4; - println!("First value of x: {x}"); - - x = borrowing(&mut x); - println!("Mutated value of x from borrowing: {x}"); - - // using slices to interact with Strings - let s: String = String::from("HelloWorld Universe"); - let slice = first_word(&s); - - println!("First Word of {}: {} and {}", s, first_word(&s), slice); -} - -fn add_suffix(mut name: String) -> String { - name.push_str(" Jr."); - name -} - -fn reference(g1: &String, g2: &String) { - println!("{g1} {g2} in a function") -} - -fn borrowing(x: &mut i32) -> i32 { - let mut v: Vec = vec![1, 2, 3]; - - println!("Vector v: {v:?}"); - - let num: &i32 = &v[2]; - - println!("The 3rd number in v: {num}"); - - v.push(4); - - // can't change v because num is still in use, and we would be changing the reference - // println!("The 3rd number in v: {num}"); // would fail - - *x + 1 -} - -fn first_word(word: &String) -> &str { - let bytes = word.as_bytes(); - - // iterate through word - for (i, &item) in bytes.iter().enumerate() { - if item == b' ' { - return &word[0..i]; - } - } - - &word[..] -} diff --git a/rust-book/ch05-structs/Cargo.toml b/rust-book/ch05-structs/Cargo.toml deleted file mode 100644 index d36dbc1..0000000 --- a/rust-book/ch05-structs/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "structs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch05-structs/README.md b/rust-book/ch05-structs/README.md deleted file mode 100644 index c40a066..0000000 --- a/rust-book/ch05-structs/README.md +++ /dev/null @@ -1,68 +0,0 @@ - -# Chapter 5 - Structs - -## Defining Structs - -Like tuples, `struct`'s can be used to encapsulate multiple data types into a single variable. What separates the two is that structs requires parameters to be named. For example: - -```Rust -struct User { - username: String, - email: String, - active: bool, - sign_in_count: u64 -} -``` - -Structs are also entirely mutable; we can't mark individual fields as mutable, it has to be the whole struct. - -## Struct update syntax - -Structs can also be created from other structs (with all the Ownership rules still applying). The `..` syntax is used to specify remaining fields that haven't been set, taking values from another struct. The `..` must always come at the end of the init - -```Rust -fn main() { - // --snip-- - - let user2 = User { - email: String::from("another@example.com"), - ..user1 - }; -} -``` - -## Struct Variations - -There are also two slight variations on the struct syntax: - -- *Tuple Struct* - - A struct with no named fields, used only to define types - - Ex, Color: `struct Color(i32, i32, i32);` tuple struct -- *Unit-Like Struct* - - A struct with no fields or names; Acts as a placeholder when we don't have any data to store as the type. This is convinient for traits, which we see more in ch10 - - Ex.: `struct AlwaysEqual;` - -## Methods - -Structs can also implement methods or functions. They can be used to return a value or perform some operation with the struct, much like a `class` for an object in other languages - -These methods are defined usings the `impl` keyword, outside and after the definition of a `struct`: - -```Rust -impl Rectangle { - fn square(size: u32) -> Self { - Self { - width: size, - height: size, - } - } - - fn area(&self) -> u32 { - self.width * self.height - } -} -``` - -We also see the use of an associated function (`square`) in the `impl` that allows us to create a new Rectangle of `square` shape via `let sq = Rectangle::square(3);` - -We can also have more than 1 `impl` block for each struct diff --git a/rust-book/ch05-structs/src/main.rs b/rust-book/ch05-structs/src/main.rs deleted file mode 100644 index 241f23f..0000000 --- a/rust-book/ch05-structs/src/main.rs +++ /dev/null @@ -1,90 +0,0 @@ -struct User { - active: bool, - username: String, - email: String, - sign_in_count: u64 -} - -// add debug trait to struct to print in console -#[derive(Debug)] -struct Rectangle { - width: u32, - height: u32 -} - -// implement a method on Rectangle struct -impl Rectangle { - fn square(size: u32) -> Self { - Self { - width: size, - height: size, - } - } - - fn area(&self) -> u32 { - self.width * self.height - } - - fn width(&self) -> bool { - self.width > 0 - } - - fn set_width(&mut self, width: u32) { - self.width = width; - } - - fn can_hold(&self, other: &Rectangle) -> bool { - self.width > other.width && self.height > other.height - } -} - -fn main() { - let user1 = User { - email: String::from("someone@example.com"), - username: String::from("John Doe"), - active: true, - sign_in_count: 1 - }; - - println!("User1: {:#?}", user1.email); - - let user2: User = build_user("else@example.com".to_string(), "Jane Doe".to_string()); - - // println!("User2 Name: {:#?}", user2.email); - - let mut rect = Rectangle { - width: 30, - height: 50 - }; - - let rect2 = Rectangle { - width: 10, - height: 40, - }; - - println!("The area of the rectangle is {} square pixels", area(&rect)); - println!("The area struct is: {:#?}", rect); - println!("Using built-in method for area {}", rect.area()); - println!("Using built-in method for if width > 0 {}", rect.width()); - println!( - "Using built-in method for if rect1 can hold rect2 {}", - rect.can_hold(&rect2) - ); - - rect.set_width(20); -} - -// declared using field init shorthand -fn build_user(email: String, username: String) -> User { - User { - active: true, - username, - email, - sign_in_count: 1, - } -} - -// area function that consumes a struct -fn area(rectangle: &Rectangle) -> u32 { - rectangle.width * rectangle.height -} \ No newline at end of file diff --git a/rust-book/ch06-enums/Cargo.toml b/rust-book/ch06-enums/Cargo.toml deleted file mode 100644 index 7b703c4..0000000 --- a/rust-book/ch06-enums/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "enums" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch06-enums/README.md b/rust-book/ch06-enums/README.md deleted file mode 100644 index 149fe6f..0000000 --- a/rust-book/ch06-enums/README.md +++ /dev/null @@ -1,80 +0,0 @@ - -# Chapter 6 - Enums - -Enums, or enumerations, allow us to define a type based on the different possible values it can take on. An enum can only be one of its values (or variants). For example, we can define a Enum for wether a value is of type V4 or V6 (different IP protocols): - -```Rust -enum IpAddrKind { - V4(String), - V6(String), -} -``` - -Enums allow us to attach data to types directly, so we can set `IpAddrKind` V4 and V6 as both String types (or different types if we wanted to) - -We can create a new variable that holds the enum type, as: - -```Rust -// create a variable of enum type -let home = IpAddr::V4(String::from("127.0.0.1")); -``` - -## The Option Enum - -Rust doesn't implement a `null` type like other languages. Instead, it uses an `Option` type to encode if a value is something or is nothing / absent (very similar to Null) - -The reason for this is because trying to reference a value, when it's null, can lead to errors. It's a very easy error to make! It has more so to do with the implementation of nulls rather than the concept. The concept is actually quite useful. - -The `Option` enum is so common that it's in the standard library, defined as: - -```Rust -enum Option { - None, // missing or absent value - Some(T), -} -``` - -The `` syntax is a generic type parameter that can be used to reference any primitive or type in Rust (more in Chapter 10) - -## The `match` Control Flow - -Like a switch statement in Scala, or a Case statement in SQL, Rust also implements a type of pattern matching code block that executes based on which pattern the input matches. For example, say we have an enum of Coins, each of a different type: - -```Rust -enum Coin { - Penny, - Nickel, - Dime, - Quarter, -} -``` - -We can write a `match` statement that allows us to execute a block of code based on which type of Coin is given - -```Rust -fn value_in_cents(coin: Coin) -> u8 { - match coin { - Coin::Penny => 1, - Coin::Nickel => 5, - Coin::Dime => 10, - Coin::Quarter => 25, - } -} -``` - -The different patterns are referred to as `arms`, and consist of a pattern and some code. The `=>` operator separates the pattern from the code - -**Note**: `match` operators are **exhaustive**, meaning they need to cover every possible outcome or type an enum can take. Rust implements a type of catch-all syntax, `other`, when we want to use the value in our code block (or `_` if we don't). - -A toy example of `other` as a catch-all statement: - -```Rust -fn value_in_cents(coin: Coin) -> u8 { - match coin { - Coin::Penny => 1, - Coin::Nickel => 5, - Coin::Dime => 10, - other => 25, - } -} -``` diff --git a/rust-book/ch06-enums/src/main.rs b/rust-book/ch06-enums/src/main.rs deleted file mode 100644 index 3b0d73f..0000000 --- a/rust-book/ch06-enums/src/main.rs +++ /dev/null @@ -1,76 +0,0 @@ -// An enum representing an ip address -#[derive(Debug)] -enum IpAddr { - V4(u8, u8, u8, u8), - V6(String), -} - -// An enum with different types -// instead of having multiple structs, we can have just one enum -#[derive(Debug)] -enum Message { - // Quit, - // Move { x: i32, y: i32 }, - Write(String), - // ChangeColor(i32, i32, i32), -} - -enum Coin { - Penny, - Nickel, - Dime, - Quarter, -} - -impl Message { - fn call(&self) { - println!("Message: {:?}", &self); - } -} - -fn main() { - - // - let home = IpAddr::V4(127, 0, 0, 1); - - println!("Home IP Address: {:?}", home); - - let loopback = IpAddr::V6(String::from("::1")); - println!("Loopback IP Address: {:?}", loopback); - - let m = Message::Write(String::from("Hello")); - m.call(); - - let penny = Coin::Penny; - println!("Value in Cents: {}", value_in_cents(penny)); - - let five = Some(5); - let six = plus_one(five); - let none = plus_one(None); - println!("Five {:?}, Six {:?}, None {:?}", five, six, none); - - let opt: Option = Some(String::from("Hello World")); - - match opt { - Some(_) => println!("Some!"), - None => println!("None!"), - } - - println!("{:?}", opt); -} - -fn value_in_cents(coin: Coin) -> u8 { - match coin { - Coin::Penny => 1, - Coin::Nickel => 5, - Coin::Dime => 10, - Coin::Quarter => 25, - } -} - -fn plus_one(x: Option) -> Option { - match x { - None => None, - Some(i) => Some(i + 1) - } -} \ No newline at end of file diff --git a/rust-book/ch07-packages/Cargo.toml b/rust-book/ch07-packages/Cargo.toml deleted file mode 100644 index e7e7ed3..0000000 --- a/rust-book/ch07-packages/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "packages-project" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch07-packages/README.md b/rust-book/ch07-packages/README.md deleted file mode 100644 index 9349ffb..0000000 --- a/rust-book/ch07-packages/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Chapter 7 - Packages, Crates, and Modules - -Packages, crates, and modules in Rust allow us to organize our code, keep certain details private while exposing others. Collectively, they are referred to as the module system that includes: - -- **Package**: A cargo feature for building, testing, and sharing crates -- **Crate**: A tree of modules that produce and executable -- **Modules** and the **use** statement: Allow us to control the scope and access to paths -- **Paths**: A way of naming items, like structs, functions, or modules - -## What is a Crate? - -A crate is the smallest amount of code the compiler with consider at runtime. It can contain modules, which can be defined in other places in our project, that get compiled into a crate - -A crate can either be a: - -- **Binary Crate**: programs that compile into an executable. Each crate must contain one, and only one, `main` function as its entry-point -- **Library Crate**: don't have main functions, and don't compile into an executable. Rather, they define features that can be shared with other projects - -The crate root is the source file from which the compiler starts file tree that governs our crate - -## What is a Package - -A package is a bundle of one of more crates that provide us with a set of functionality. It contains a cargo.toml file that describes how to build those crates. Cargo is actually a package itself, that compiles our binary crates in an executable. It also has library crates which the binary crate depends on, that can also be shared with other programs - -`main.rs` is always interpreted as the crate root of a binary crate with the same name as the package. If, instead, we had a `src/lib.rs` file, our package contains a library crate with the same name as the package, and it's the crate root. - -## Defining a Module - -- **Declaring Modules**: In the root of the crate, we can declare new modules. The compiler will then look for the definition of the crate in the following patter: - - Inline, within the curly brackets that follow the `mod` keyword - - In a file named after the module - - In a file under the module folder named `mods.rs` - -For example, if we declared a `garden` module with `mod garden;`, we would do one of the above to define the code associated with our module - -- **Declaring submodules**: We can declare submodules in any other crate other than the root. For example, if we had a file for our garden module, `src/garden.rs`, we can declare a vegetables submodule as `mod vegetables;` - - - This submodule can be stored in a separate file or directly in the `garden.rs` file - -- **Private vs. Public**: By default, any code not in the crate root is private. We have to explicitly declare a module as public, with the `pub` keyword, if we want to access it in other parts of our program. This applies to modules (`pub mod`), to structs (`pub struct`), to enums (`pub enum`), and to functions (`pub fn`) - - - Fields inside of a struct are also private by default and need to be tagged with `pub` if we want to access them directly outside of the module file - - `impl` don't need the `pub` tag, but their functions do - -- **`use` Keyword**: When referring to a module outside of its declaration file, we have to reference it by its full path. For example, if we had a `struct Asparagus { }` in our vegetables.rs, we would need to reference it as: `crate::garden::vegetables::Asparagus;`. If we use the keyword `use crate::garden::vegetables::Asparagus` , we can instead call `Asparagus { }` directly - - This saves us on the verbose declaration of submodules diff --git a/rust-book/ch07-packages/src/garden.rs b/rust-book/ch07-packages/src/garden.rs deleted file mode 100644 index 5c9ab6a..0000000 --- a/rust-book/ch07-packages/src/garden.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod vegetables; - -pub fn water_plants(){ - println!("Watering your plants!") -} - -pub fn harvest_plants(){ - println!("Harvesting your plants!") -} diff --git a/rust-book/ch07-packages/src/garden/vegetables.rs b/rust-book/ch07-packages/src/garden/vegetables.rs deleted file mode 100644 index 4a5434e..0000000 --- a/rust-book/ch07-packages/src/garden/vegetables.rs +++ /dev/null @@ -1,36 +0,0 @@ -#[derive(Debug)] -enum Age { - New, - OneWeekOld, - ThreeWeeksOld, - OneMonthOld, - Dead -} - -#[derive(Debug)] -pub struct Asparagus { - count: u32, - age: Age -} - -impl Asparagus { - pub fn new() -> Self { - Asparagus { - count: 1, - age: Age::New - } - } - - pub fn grow(&mut self) { - print!("Growing from {:?} ", self.age); - self.age = match self.age { - Age::New => Age::OneWeekOld, - Age::OneWeekOld => Age::ThreeWeeksOld, - Age::ThreeWeeksOld => Age::OneMonthOld, - Age::OneMonthOld => Age::Dead, - _ => Age::Dead - }; - - println!("to {:?}", self.age) - } -} \ No newline at end of file diff --git a/rust-book/ch07-packages/src/main.rs b/rust-book/ch07-packages/src/main.rs deleted file mode 100644 index 17b43a8..0000000 --- a/rust-book/ch07-packages/src/main.rs +++ /dev/null @@ -1,19 +0,0 @@ -// importing the Asparagus struct -use crate::garden::vegetables::{Asparagus}; - -pub mod garden; - -fn main() { - let mut plant = Asparagus::new(); - println!("Hello, I am growing {:?}", plant); - - plant.grow(); - - garden::water_plants(); - plant.grow(); - garden::water_plants(); - plant.grow(); - garden::water_plants(); - - garden::harvest_plants(); -} diff --git a/rust-book/ch08-collections/Cargo.toml b/rust-book/ch08-collections/Cargo.toml deleted file mode 100644 index 482838c..0000000 --- a/rust-book/ch08-collections/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "collections" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch08-collections/README.md b/rust-book/ch08-collections/README.md deleted file mode 100644 index 028548a..0000000 --- a/rust-book/ch08-collections/README.md +++ /dev/null @@ -1,115 +0,0 @@ -## Chapter 8 - Collections - -In Rust, collections are data structures that can hold multiple values, similar to arrays or tuples in other languages. Collections hold their data in the heap, allowing the size of the data structure to grow and shrink as the program needs - -There are 3 main types of collections discussed in this chapter: - -- **Vectors**: (similar to dynamic arrays) can store a variable number of values in a single data structure -- **Strings**: a collection of characters, the `String` type we interact with frequently -- **HashMaps**: a key-value pair mapping for speedy lookup of data. In rust, this is an more general implementation of a map - -## Storing Values in Vectors - -The syntax for vectors is `Vec`, where T is a placeholder for a particular type. Vectors hold a variable number of values right next to each other in memory, but it can **only store values of the same type**. Vectors are smart in that they can also infer the data types from a given input (as supposed to defining the type, as below) - -Example: - -- `let v: Vec = Vec::new();` Creates a new empty vector of type i32 -- `let v = vec![1, 2, 3, 4];` Create a new vector from a list of value, type inferred - -### Reading a Vector - -We can reference values in a vector in two ways, either via indexing (square brackets) or using the `get` method - -- If we reference an out-of-bound index, using indexing, it will cause our program to panic (as expected) -- If we instead use the `get` method, it returns an `Option` value from which we can use the `None` value to reference out-of-bounds indexes - -Example: - -- Indexing: `let y = v[0];` - reference but not taking ownership add `&` -- `get` method: `let y = v.get(0)` - -We can also iterate and enumerate our way through a vector, as below: - -```Rust -// This program panics -fn main(){ - let v: Vec = vec![1, 2, 3, 4]; - for n_ref in v.iter() { - println!("{n_ref}"); - } -} -``` - -**Note**: `iter` takes a immutable reference to `v` or the vector, and we can't modify that vector within the loop (otherwise it will deallocate the previous pointer) - -### Inserting Values into a Vector - -We can use the `push` method to insert new values into our vector - -### Using Enums with Vectors - -Sometimes storing the same type in vectors can be inconvenient at times. Luckily there's a workaround with enums. As enums are defined all as the same type, we can easily return different types under the enum variants! - -Example: - -```Rust - enum SpreadsheetCell { - Int(i32), - Float(f64), - Text(String), - } - - let row = vec![ - SpreadsheetCell::Int(3), - SpreadsheetCell::Text(String::from("blue")), - SpreadsheetCell::Float(10.12), - ]; - -``` - -## Strings - -The `String` type is provided with the Rust standard library, different to the built-in string type `str` slice we usually see in its borrowed form `&str` - -The `String` type stores its data in the heap, and so is growable, mutable, and owned - -- Strings are actually implement as a wrapper around a vector of bytes that come with some extra guarantees, restrictions, and capabilities - - We can push more data onto the String, we can convert a `"String"` into a String type vector using the `to_string` method, and it implements the `Display` trait - - `push_str` take a string slice, because we don't necessarily want to take ownership of the str we're pushing -- We can concatenate strings with the `+` operator or the `format!` macro -- Strings don't support indexing, instead we need to iterate over a sequence of chars using the `chars` method - - This is because the way strings are stored as vectors of bytes, a single character may not map to a single byte in the vector - -Example: Iterating over a String - -```Rust -for c in "Hello".chars() { - println!("{c}"); -} -``` - -One thing to note about String is how they are encoded. Strings in Rust use the UTF-8 encoding, and that's why reading the byte vector may not always return the value we expect from a string - -## HashMaps - -The last common collection in Rust is the `HashMap`, which stores a mapping of the key `K` to a value `V` using a hashing function. Similar to a dictionary in Python - -```Rust -use std::collections::HashMap; - -let mut scores = HashMap::new(); - -scores.insert(String::from("Blue"), 10); -scores.insert(String::from("Yellow"), 50); - -let team_name = String::from("Blue"); -let score = scores.get(&team_name).copied().unwrap_or(0); -``` - -When inserting values into a hash map, if the type implements the Copy trait its values are copied directly, otherwise (like String) it takes ownership - -HashMaps also come with methods to surface capabilities we find Python, like inserting a new value if the key doesn't exist or updating its value if it does. - -- This is done via the `entry` api that checks the key we give it, and the return value is an enum called Entry that may or may not exist -- Using the `or_insert` , we can check if the key exists, and if not, insert the value with the given key diff --git a/rust-book/ch08-collections/src/main.rs b/rust-book/ch08-collections/src/main.rs deleted file mode 100644 index c2dc2c9..0000000 --- a/rust-book/ch08-collections/src/main.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::collections::HashMap; - -fn main() { - println!("Hello, world!"); - - let mut v: Vec = Vec::new(); - - v.push(5); - - println!("What's on V: {:?}", v); - - let v = vec![1, 2, 3, 4, 5]; - let third: &i32 = &v[2]; - println!("Third value: {third}"); - - let third: Option<&i32> = v.get(2); - match third { - Some(third) => println!("The third element is {third}"), - None => println!("There is no third element"), - } - - // let mut v = vec![1, 2, 3]; - // dup_in_place(&mut v); - // - - let s = String::new(); - println!("{s}"); - - let data = "initial comment"; - let s = data.to_string(); - - println!("{s}"); - - let mut s = String::from("initial value"); - s.push_str(" baar"); - - println!("{s}"); - - // Hash Maps - let mut scores = HashMap::new(); - - scores.insert(String::from("Blue"), 10); - scores.insert(String::from("Red"), 50); - - let team_name = String::from("Blue"); - let score = scores.get(&team_name).copied().unwrap_or(0); - - println!("Retrieved Score: {score}"); - - for (key, value) in &scores { - println!("{key}: {value}"); - } - - scores.entry(String::from("Yellow")).or_insert(50); - - println!("{:?}", scores); - - //let mut v: Vec = vec![1, 2]; - - // for mut n_ref in 0 .. v.len() { - // n_ref += 1 - - // println!("Modified value: {}", n_ref); - // } -} - -// Running this function would cause Rust to panic -//fn dup_in_place(v: &mut Vec){ -// // we can't mutate v when we use an iterator -// // because Rust will deallocate the old reference when we add new data -// for n_ref in v.iter(){ -// v.push(*n_ref); // duplicate the value in place -// } -//} diff --git a/rust-book/ch09-error-handling/Cargo.toml b/rust-book/ch09-error-handling/Cargo.toml deleted file mode 100644 index fbb7411..0000000 --- a/rust-book/ch09-error-handling/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "error_handling" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch09-error-handling/README.md b/rust-book/ch09-error-handling/README.md deleted file mode 100644 index b2ab59a..0000000 --- a/rust-book/ch09-error-handling/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Chapter 9 - Error handling - -In Rust, there are 2 ways to handle errors: - -- **Recoverable** - . - When we want to report back to the user that the operation failed but they can continue to use the program and retry the operation - - Instead of ending the program, Rust can return a `Result` enum with the variants `O` and `Err` - - This allows us to customize the actions or returns upon failure - -The Result Enum: - -```Rust -enum Result { - Ok(T), - Err(E), -} -``` - -which allows us to write programs like the following that return an Error if we can't find a file called "hello.txt" - -```Rust -use std::fs::File; - -fn main() { - let greeting_file_result = File::open("hello.txt"); - - let greeting_file = match greeting_file_result { - Ok(file) => file, - Err(error) => panic!("Problem opening the file: {:?}", error), - }; -} -``` - -We can even take this a step further and `match` different types of Errors: - -```Rust -use std::fs::File; -use std::io::ErrorKind; - -fn main() { - let greeting_file_result = File::open("hello.txt"); - - let greeting_file = match greeting_file_result { - Ok(file) => file, - Err(error) => match error.kind() { - ErrorKind::NotFound => match File::create("hello.txt") { - Ok(fc) => fc, - Err(e) => panic!("Problem creating the file: {:?}", e), - }, - other_error => { - panic!("Problem opening the file: {:?}", other_error); - } - }, - }; -} -``` - -- **Unrecoverable** - - When we want the program to exit, as the result of a bug or trying to access a value in beyond the end of an array - - This is usually done with a `panic!` macro - - By default, `panic` causes our program to unwind. That is, Rust walks back up the stack and cleans up the data for each function. This can be a lot of work, so there is also the alternative of aborting immediately under a panic which just ends the program without cleaning up - - Done by setting `panic = 'abort'` in the `[profile.release]` of our toml - - There are shortcuts for calling Panic on an Error - - `unwrap` - - Shortcuts the match statement by returning the result or calls panic if it Err's - - `expect` - - Similar to unwrap, except we can choose the output of the error message - -## Propagating Error - -Writing match statements that Err when something fails is very common in Rust. Instead of handling the error in the function, we can actually return it to the calling code and let it decide what to do. This is called *propagating errors* - -Example: - -```Rust -use std::fs::File; -use std::io::{self, Read}; - -fn read_username_from_file() -> Result { - let username_file_result = File::open("hello.txt"); - - let mut username_file = match username_file_result { - Ok(file) => file, - Err(e) => return Err(e), - }; - - let mut username = String::new(); - - match username_file.read_to_string(&mut username) { - Ok(_) => Ok(username), - Err(e) => Err(e), - } -} -``` - -This is so common, there's actually a shortcut operation called `?` to make this simpler. Rewriting the above code, gives us: - -```Rust -use std::fs::File; -use std::io::{self, Read}; - -fn read_username_from_file() -> Result { - let mut username = String::new(); - - File::open("hello.txt")?.read_to_string(&mut username)?; - - Ok(username) -} -``` - -**Note**: `?` can only be used in functions whose return type is compatible with `?` (that is Ok and Err `Results`, `Options`, or another implementation of `FromResidual`) diff --git a/rust-book/ch09-error-handling/src/main.rs b/rust-book/ch09-error-handling/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/rust-book/ch09-error-handling/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/rust-book/ch10-generics/Cargo.toml b/rust-book/ch10-generics/Cargo.toml deleted file mode 100644 index 2a85b1d..0000000 --- a/rust-book/ch10-generics/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "generics" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch10-generics/README.md b/rust-book/ch10-generics/README.md deleted file mode 100644 index b3613cf..0000000 --- a/rust-book/ch10-generics/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Chapter 11 - Automated Tests - -In Rust, tests are organized into two main categories: **unit tests** and **integration tests** - -- **Unit Tests** - - Embedded into our `src/lib.rs` directly, they're meant to be small focused tests for a single module - - Convention is to create a `mode test {...}` in the file that contains the tests we want - - Has access to private function, being in the same file as the library - - Starts with `#[cfg(test)]` which tells rust to ignore tests unless explicitly called via the `cargo test` (i.e. `cargo run` or `cargo build` do not run theses tests) - - This saves time on compile, so we don't load our library with tests - - Each test is labeled with a `#[test]`, and written as a `fn` - - `cfg` stands for configuration - -```Rust -#[cfg(test)] -mod tests { - #[test] - fn exploration() { - assert_eq!(2 + 2, 4); - } -} -``` - -- **Integration Tests** - - Integration tests are stored externally to our library (unlike Unit Tests) - - This allows us to bring in multiple parts of our library's public API, and test if they all work together as designed - - Stored in a `tests/` directory - - Each file in the `tests` directory acts like a separate crate, and we need to bring all the dependencies into scope for each crate - -## Using `assert!` Macros - -Rust provides an `asset!` macro that helps standardize conditions that we want to evaluate to `true` - -- This is the same as using a block `if` statement to check if output is `true` - -Rust also takes very common tests like asserting if two values are equal or not equal, and packaged them into macros: `assert_eq!` and `assert_neq!` - -Each macro can also take a custom message, so that we can add more details to on failure: -`assert_eq!(left_result, right_result, custom_message);` diff --git a/rust-book/ch10-generics/src/main.rs b/rust-book/ch10-generics/src/main.rs deleted file mode 100644 index 1724d18..0000000 --- a/rust-book/ch10-generics/src/main.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::{cmp::PartialOrd, fmt::Display}; - -#[derive(Debug)] -struct Point { - x: T, - y: T, -} - -fn main() { - println!("Hello, world!"); - - // find the largest integer using largest_i32 - let vec = vec![1, 5, 100, 2, 10]; - - println!("Largest value is: {}", largest_i32(&vec)); - - // using our generic function - println!("Largest generic value is: {}", largest(&vec)); - - let pt = Point { x: 1, y: 10 }; - - println!("{:?}, {}, {}", pt, pt.x, pt.y); - - let pair = Pair::new(1, 10); - pair.cmp_display(); -} - -// Using generics instead of types as input params -// we need to restrict T to being comaparable -fn largest(list: &[T]) -> &T { - let mut largest = &list[0]; - - for item in list { - if item > largest { - largest = item; - } - } - - largest -} - -// Using explicit types as parameters to functions -fn largest_i32(list: &[i32]) -> &i32 { - let mut largest = &list[0]; - for item in list { - if item > largest { - largest = item; - } - } - - largest -} - -// Traits -struct Pair { - x: T, - y: T, -} - -impl Pair { - fn new(x: T, y: T) -> Self { - Self { x, y } - } -} - -impl Pair { - fn cmp_display(&self) { - if self.x >= self.y { - println!("The largest member is x = {}", self.x); - } else { - println!("The largest member is y = {}", self.y); - } - } -} diff --git a/rust-book/ch11-automated-tests/Cargo.toml b/rust-book/ch11-automated-tests/Cargo.toml deleted file mode 100644 index 5458058..0000000 --- a/rust-book/ch11-automated-tests/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "automated_tests" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch11-automated-tests/README.md b/rust-book/ch11-automated-tests/README.md deleted file mode 100644 index 5e19da8..0000000 --- a/rust-book/ch11-automated-tests/README.md +++ /dev/null @@ -1,40 +0,0 @@ - -# Chapter 11 - Automated Tests - -In Rust, tests are organized into two main categories: **unit tests** and **integration tests** - -- **Unit Tests** - - Embedded into our `src/lib.rs` directly, they're meant to be small focused tests for a single module - - Convention is to create a `mode test {...}` in the file that contains the tests we want - - Has access to private function, being in the same file as the library - - Starts with `#[cfg(test)]` which tells rust to ignore tests unless explicitly called via the `cargo test` (i.e. `cargo run` or `cargo build` do not run theses tests) - - This saves time on compile, so we don't load our library with tests - - Each test is labeled with a `#[test]`, and written as a `fn` - - `cfg` stands for configuration - -```Rust -#[cfg(test)] -mod tests { - #[test] - fn exploration() { - assert_eq!(2 + 2, 4); - } -} -``` - -- **Integration Tests** - - Integration tests are stored externally to our library (unlike Unit Tests) - - This allows us to bring in multiple parts of our library's public API, and test if they all work together as designed - - Stored in a `tests/` directory - - Each file in the `tests` directory acts like a separate crate, and we need to bring all the dependencies into scope for each crate - -## Using `assert!` Macros - -Rust provides an `asset!` macro that helps standardize conditions that we want to evaluate to `true` - -- This is the same as using a block `if` statement to check if output is `true` - -Rust also takes very common tests like asserting if two values are equal or not equal, and packaged them into macros: `assert_eq!` and `assert_neq!` - -Each macro can also take a custom message, so that we can add more details to on failure: -`assert_eq!(left_result, right_result, custom_message);` diff --git a/rust-book/ch11-automated-tests/src/lib.rs b/rust-book/ch11-automated-tests/src/lib.rs deleted file mode 100644 index 9576e47..0000000 --- a/rust-book/ch11-automated-tests/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod rectangle; diff --git a/rust-book/ch11-automated-tests/src/rectangle.rs b/rust-book/ch11-automated-tests/src/rectangle.rs deleted file mode 100644 index 1551dd1..0000000 --- a/rust-book/ch11-automated-tests/src/rectangle.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub struct Rectangle { - pub width: u32, - pub length: u32, -} - -impl Rectangle { - pub fn can_hold(&self, rect2: &Rectangle) -> bool { - self.width >= rect2.width && self.length >= rect2.length - } -} diff --git a/rust-book/ch11-automated-tests/tests/rectangle.rs b/rust-book/ch11-automated-tests/tests/rectangle.rs deleted file mode 100644 index 90b23c0..0000000 --- a/rust-book/ch11-automated-tests/tests/rectangle.rs +++ /dev/null @@ -1,32 +0,0 @@ -#[cfg(test)] -use automated_tests::rectangle::Rectangle; - -#[test] -fn larger_can_hold_smaller() { - let larger = Rectangle { - width: 20, - length: 25, - }; - - let smaller = Rectangle { - width: 10, - length: 15, - }; - - assert!(larger.can_hold(&smaller)); -} - -#[test] -fn smaller_cant_hold_larger() { - let larger = Rectangle { - width: 20, - length: 25, - }; - - let smaller = Rectangle { - width: 10, - length: 15, - }; - - assert!(!smaller.can_hold(&larger)); -} diff --git a/rust-book/ch12-minigrep/Cargo.toml b/rust-book/ch12-minigrep/Cargo.toml deleted file mode 100644 index a256566..0000000 --- a/rust-book/ch12-minigrep/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "minigrep" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch12-minigrep/README.md b/rust-book/ch12-minigrep/README.md deleted file mode 100644 index 03b0882..0000000 --- a/rust-book/ch12-minigrep/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Chapter 12 - IO Project - -This is a simple IO project that replicates `grep` but with Rust. - -The project is fairly straightforward, taking command line arguments for a query and a file path to search in. This program combines a few topics such as Error Handling, Automated Tests, and handling lifetimes for objects. - -How to run: - -```zsh -cargo run -- to poem.txt -``` diff --git a/rust-book/ch12-minigrep/output.txt b/rust-book/ch12-minigrep/output.txt deleted file mode 100644 index d40e5a2..0000000 --- a/rust-book/ch12-minigrep/output.txt +++ /dev/null @@ -1,2 +0,0 @@ -Are you nobody, too? -How dreary to be somebody! diff --git a/rust-book/ch12-minigrep/poem.txt b/rust-book/ch12-minigrep/poem.txt deleted file mode 100644 index 8707527..0000000 --- a/rust-book/ch12-minigrep/poem.txt +++ /dev/null @@ -1,9 +0,0 @@ -I'm nobody! Who are you? -Are you nobody, too? -Then there's a pair of us - don't tell! -They'd banish us, you know. - -How dreary to be somebody! -How public, like a frog -To tell your name the livelong day -To an admiring bog! diff --git a/rust-book/ch12-minigrep/sample.txt b/rust-book/ch12-minigrep/sample.txt deleted file mode 100644 index 980a0d5..0000000 --- a/rust-book/ch12-minigrep/sample.txt +++ /dev/null @@ -1 +0,0 @@ -Hello World! diff --git a/rust-book/ch12-minigrep/src/lib.rs b/rust-book/ch12-minigrep/src/lib.rs deleted file mode 100644 index 6b434d4..0000000 --- a/rust-book/ch12-minigrep/src/lib.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::error::Error; -use std::env; -use std::fs; - -pub struct Config { - pub query: String, - pub file_path: String, - pub ignore_case: bool, -} - -impl Config { - pub fn new(args: &[String]) -> Result { - if args.len() < 3 { - return Err("not enough arguments"); - } - - let query = args[1].clone(); - let file_path = args[2].clone(); - let ignore_case = env::var("IGNORE_CASE").is_ok(); - - Ok(Config { - query, - file_path, - ignore_case, - }) - } -} - -pub fn run(config: Config) -> Result<(), Box> { - //Box means the function will return a type that implements the Error trait - let contents = fs::read_to_string(config.file_path)?; - - let results = if config.ignore_case { - search_case_insensitive(&config.query, &contents) - } else { - search(&config.query, &contents) - }; - - for line in results { - println!("{line}"); - } - - Ok(()) -} - -// We want the returned value to live as long as the input contents from the file -pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { - let mut results = Vec::new(); - - for line in contents.lines() { - // check if the line contains the query - if line.contains(query){ - results.push(line.trim()) - } - } - - results -} - -pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { - let query = query.to_lowercase(); - let mut results = Vec::new(); - - for line in contents.lines() { - if line.to_lowercase().contains(&query) { - results.push(line.trim()); - } - } - - results -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn case_sensitive() { - let query = "duct"; - let contents = "\ - Rust: - safe, fast, productive. - Pick three."; - - assert_eq!(vec!["safe, fast, productive."], search(query, contents)) - } - - #[test] - fn case_insensitive() { - let query = "rUsT"; - let contents = "\ - Rust: - safe, fast, productive. - Pick three. - Trust me."; - - assert_eq!(vec!["Rust:", "Trust me."], search_case_insensitive(query, contents)); - } -} diff --git a/rust-book/ch12-minigrep/src/main.rs b/rust-book/ch12-minigrep/src/main.rs deleted file mode 100644 index 457de17..0000000 --- a/rust-book/ch12-minigrep/src/main.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::env; -use std::process; - -use minigrep::Config; - -fn main() { - let args: Vec = env::args().collect(); - - let config = Config::new(&args).unwrap_or_else(|_err| { - Config { - query: "the".to_owned(), - file_path: "poem.txt".to_owned(), - ignore_case: false - } - // eprintln!("Problems parsing arguments: {err}"); - // process::exit(1); - }); - - eprintln!("Searching for {}", config.query); - eprintln!("in file {}", config.file_path); - - // Because we run doesn't return anything useful, we mainly called it for - // its eprintln statement we can instead use it to handle - // a possible returned Error - if let Err(e) = minigrep::run(config) { - eprintln!("Error reading the file: {e}"); - process::exit(1); - } -} diff --git a/rust-book/ch13-closures/Cargo.toml b/rust-book/ch13-closures/Cargo.toml deleted file mode 100644 index 96e4b8a..0000000 --- a/rust-book/ch13-closures/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "ch13-closures" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/rust-book/ch13-closures/README.md b/rust-book/ch13-closures/README.md deleted file mode 100644 index e649a41..0000000 --- a/rust-book/ch13-closures/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# Closures - -Rust inherits some Functional Language features in the form of Closures, or anonymous functions. Very similar t a lambda function in python, we can use a closure to perform some mapping, filtering, etc.. operations on a sequence of data. - -Unlike functions, closures are evaluated in the scope they're defined, and they can infer their data types from a given input. For example, the `unwrap_or_else` method can take a closure as an action on the `_or_else` side-effect (something we've already seen). This allows us to perform some custom actions when the `unwrap` returns an Error, like when a value does not exists. - -```Rust -let s = Some(String::from("Hello World")); -let output = s.unwrap_or_else(|| String::from("Hello Universe")); - -println!("{output}"); -``` - -In this toy example, if we set s to `None`, the unwrap clause would call our closure and return `Hello Universe` instead! - -Closures can also borrow, borrow mutably, or take ownership of a given input parameter. It depends on the what the body of the function does. This plays a large role when implement concurrent features, as we will see more in Ch16. -- We can always force ownership too with the `move` command - -## Using Lifetimes - -When we write functions that return closures, things get a little more complicated. Not only do we need to be aware of the different types of return closures, namely `Fn, FnOnce, FnMut`, but we also have to associate a Lifetime with the return value (because our reference can't outlive the data) -- `FnOnce` - Closure is called only once -- `FnMut` - Closure mutates values in its body, and can be called more than once -- `Fn` - Don't move or mutate values in the body, but also take nothing from their env - -For example, a function that makes a clone of a string and returns the reference - -```Rust -// vvvv vv vvvv -fn make_a_cloner<'a>(s_ref: &'a str) -> impl Fn() -> String + 'a { - move || s_ref.to_string() -} -``` - -Where `'a` represents the input reference - -# Iterators - -We've seen iterators in earlier chapters as well, but here we add a little more details. - -Iterators are lazy, and they don't take effect until we call a method that consumes the iterator. This can either be a reduce function, like `sum`, or using the `collect` function to consumer the iterator. The `for` loop also consumes an iterator, which is why we've never had to call `next` within a for loop! - -```Rust -let v: Vec = vec![1, 2, 3, 4]; - -// Create iterator from v -let iterator = v.iter(); - -// Loop over values, mutiplying each by 2 -let output = iterator.map(|x| x * 2).collect::>(); - -println!("{:?} {:?}", output, v); -``` - -All iterators implement the `Iterator` trait and the `next` method, which allows us to move through the iterator elements. -- `next` is called a consuming adaptor, because calling next allows us to use up the iterator - -Other method that produce iterators, instead of consume them, are called iterator adaptors. For example, out `map` function is a iterator adaptor. We have to `collect` the values before the `map` function can be evaluated diff --git a/rust-book/ch13-closures/src/main.rs b/rust-book/ch13-closures/src/main.rs deleted file mode 100644 index f7a2ae3..0000000 --- a/rust-book/ch13-closures/src/main.rs +++ /dev/null @@ -1,28 +0,0 @@ -fn main() { - println!("Hello, world!"); - - let s = None; // Some(String::from("Hello World")); - let output = s.unwrap_or_else(|| String::from("Hello Universe")); - - println!("{output}"); - - // Mutable Closure - let mut v = vec![1, 2, 3, 4]; - let mut borrows_mutably = || v.push(10); - borrows_mutably(); - - // Using a for loop as consuming adaptor (or calls to .next()) - for value in [1, 2, 3, 4] { - println!("{}", value * 2); - } - - // Using iterator adaptor to manipulate values - let v: Vec = vec![1, 2, 3, 4]; - let iterator = v.iter(); - - println!("{:?}", v); - - let output = iterator.map(|x| x * 2).collect::>(); - - println!("{:?} {:?}", output, v); -} diff --git a/rust-book/ch15-smart-pointers/README.md b/rust-book/ch15-smart-pointers/README.md deleted file mode 100644 index 0afed51..0000000 --- a/rust-book/ch15-smart-pointers/README.md +++ /dev/null @@ -1,53 +0,0 @@ - -# Ch15 - Smart Pointers - - -Smart pointers are data structure that behave like pointers (references) but come with additional metadata and capabilities that help us adapt to specific scenarios - - - The main difference between a reference and a smart pointer is that the smart pointer will also own the data it points to - - For example, `Strings` and `Vec` are take advantage of smart pointer like features, but are still classified as collections - - Smart Pointers are built as `structs`. They implement the `Deref` and `Drop` traits that allow us to control reference behaviours - - `Deref` = allows an instance of a struct smart pointer to behave like a normal reference - - `Drop` = allows us to customize how code runs when our smart pointer goes out of scope - -In the standard library, there are 3 common smart pointers we can use (alongside creating our own): - - - `Box` = for storing values on the heap, mutable or immutable - - `Rc` = for reference counting for immutable borrows with multiple owners - - `Ref` and `RefMut` , via `RefCell` , that allows us to delay borrowing rules to runtime instead of compile time - - This allows us to do certain actions that would not be allow by the borrow checker at compile time - -## Using `Box` - -The simplest smart \pointer, `Box` allows us to point to data on the heap rather than on the stack, with just a pointer to the heap data stored on the stack - -Example Box: - -```Rust -fn main() { - let b = Box::new(5); - println!("b = {}", b); -} -``` - -The downside to a Box data structure is that it is single ownership based, so we end up transferring ownership on reference. - -## Using `Rc` - -Also called reference counting, it's an extension to storing data on the heap, `Rc` , allows us to have multiple (immutable) references and keeps track of each. When there are no more references to a value, it can clean up the smart pointer without any references being lost. - -However, like `Box` , these are single-thread data structures that shouldn't be used in multi-threaded contexts. - -## Using `RefCell` - -While `Rc` gives us an immutable reference, there are times when we would want to use mutable ones to internal data. This is called interior mutability, which allows us to mutate data even if there are immutable references to that data (something not typically allows with the borrow checker) - -# Summary - -How do we go about choosing between `Box`, `Rc`, and `RefCell`? - -- `Rc` allows us to have multiple owners of the same data; `Box` and `RefCell` only allow single ownership -- `Box` allows immutable or mutable borrows, checked at runtime; `Rc` does immutable borrow checked at compile time; `RefCell` allows immutable or mutable borrow checked at runtime - - `RefCell` allows us to mutate values inside the data structure even when it is immutable - -The downside to `Rc` and `RefCell` is that we can create Memory Leaks, where memory is created but never cleaned up.