diff --git a/code/02_data_types/01_integers/integer_vars/Cargo.lock b/code/02_data_types/01_integers/integer_vars/Cargo.lock new file mode 100644 index 0000000..cfbd94e --- /dev/null +++ b/code/02_data_types/01_integers/integer_vars/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "integer_vars" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/code/02_data_types/01_integers/integer_vars/Cargo.toml b/code/02_data_types/01_integers/integer_vars/Cargo.toml new file mode 100644 index 0000000..f1c8fbe --- /dev/null +++ b/code/02_data_types/01_integers/integer_vars/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "integer_vars" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "*" diff --git a/code/02_data_types/01_integers/integer_vars/src/int_operations.rs b/code/02_data_types/01_integers/integer_vars/src/int_operations.rs new file mode 100644 index 0000000..690aa60 --- /dev/null +++ b/code/02_data_types/01_integers/integer_vars/src/int_operations.rs @@ -0,0 +1,34 @@ +// Function to add two integers and return the result. +pub fn add_ints() { + let a = 10; + let b = 5; + println!("The sum of two ints is {}", a + b) +} + +// Function to subtract the second integer from the first and return the result. +pub fn sub_ints() { + let a = 10; + let b = 5; + println!("The subtraction result is {}", a - b) +} + +// Function to multiply two integers and return the result. +pub fn mul_ints() { + let a = 10; + let b = 5; + println!("The multiplication results is {}", a * b) +} + +// Function to divide the first integer by the second and return the result. +pub fn divide_ints() { + let a = 7; + let b = 3; + println!("The integer division result is {}", (a / b)) +} + +// Function to calculate the remainder when the first integer is divided by the second. +pub fn remainder_ints() { + let a = 7; + let b = 3; + println!("The remainder of the division is {}", (a % b)); +} diff --git a/code/02_data_types/01_integers/integer_vars/src/main.rs b/code/02_data_types/01_integers/integer_vars/src/main.rs new file mode 100644 index 0000000..fc04df7 --- /dev/null +++ b/code/02_data_types/01_integers/integer_vars/src/main.rs @@ -0,0 +1,116 @@ +mod int_operations; +mod rnd_integers; + +fn main() { + // Invoke the print_signed_integers() function + make_separator(); + print_signed_integers(); + make_separator(); + signed_integers_size(); + make_separator(); + + // Invoke unsigned functions + print_unsigned_integers(); + make_separator(); + unsigned_integers_size(); + make_separator(); + + // Generate a random signed integers + rnd_integers::print_rnd_integers(); + + // Integer Operations + make_separator(); + int_operations::add_ints(); + int_operations::sub_ints(); + int_operations::mul_ints(); + int_operations::divide_ints(); + int_operations::remainder_ints(); + make_separator(); +} + +// Simple function that prints `=` to pretify the console output +fn make_separator() { + println!("{}", "*".repeat(52)); +} + +// a function that prints signed integers +fn print_signed_integers() { + let i8_val: i8 = -123; + let i16_val: i16 = -12345; + let i32_val: i32 = -12345678; + let i64_val: i64 = -1234567890; + + println!("i8 : {}", i8_val); + println!("i16: {}", i16_val); + println!("i32: {}", i32_val); + println!("i64: {}", i64_val); +} + +fn signed_integers_size() { + let i8_val: i8 = -123; + let i16_val: i16 = -12345; + let i32_val: i32 = -12345678; + let i64_val: i64 = -1234567890; + + println!( + "i8 : {:<12} (Size: {} bytes)", + i8_val, + std::mem::size_of::() + ); + println!( + "i16: {:<12} (Size: {} bytes)", + i16_val, + std::mem::size_of::() + ); + println!( + "i32: {:<12} (Size: {} bytes)", + i32_val, + std::mem::size_of::() + ); + println!( + "i64: {:<12} (Size: {} bytes)", + i64_val, + std::mem::size_of::() + ); +} + +// A function that print unsigned integers +fn print_unsigned_integers() { + let u8_val: u8 = 123; + let u16_val: u16 = 12345; + let u32_val: u32 = 12345678; + let u64_val: u64 = 1234567890; + + println!("u8: {}", u8_val); + println!("u16: {}", u16_val); + println!("u32: {}", u32_val); + println!("u64: {}", u64_val); +} + +fn unsigned_integers_size() { + let u8_val: u8 = 123; + let u16_val: u16 = 12345; + let u32_val: u32 = 12345678; + let u64_val: u64 = 1234567890; + + println!( + "u8: {:<12} (Size: {} bytes)", + u8_val, + std::mem::size_of::() + ); + println!( + "u16: {:<12} (Size: {} bytes)", + u16_val, + std::mem::size_of::() + ); + println!( + "u32: {:<12} (Size: {} bytes)", + u32_val, + std::mem::size_of::() + ); + println!( + "u64: {:<12} (Size: {} bytes)", + u64_val, + std::mem::size_of::() + ); +} diff --git a/code/02_data_types/01_integers/integer_vars/src/rnd_integers.rs b/code/02_data_types/01_integers/integer_vars/src/rnd_integers.rs new file mode 100644 index 0000000..4c425da --- /dev/null +++ b/code/02_data_types/01_integers/integer_vars/src/rnd_integers.rs @@ -0,0 +1,22 @@ +use rand; +// Advanced Section +// ================ + +pub fn generate_random_signed_integer() -> T +where + rand::distributions::Standard: rand::distributions::Distribution, +{ + rand::random() +} +// Print random generated signed integers +pub fn print_rnd_integers() { + let random_integer_i8: i8 = generate_random_signed_integer(); + let random_integer_i16: i16 = generate_random_signed_integer(); + let random_integer_i32: i32 = generate_random_signed_integer(); + let random_integer_i64: i64 = generate_random_signed_integer(); + + println!("Random i8 : {}", random_integer_i8); + println!("Random i16: {}", random_integer_i16); + println!("Random i32: {}", random_integer_i32); + println!("Random i64: {}", random_integer_i64); +} diff --git a/code/02_data_types/02_floats/float_vars/Cargo.lock b/code/02_data_types/02_floats/float_vars/Cargo.lock new file mode 100644 index 0000000..54a99ee --- /dev/null +++ b/code/02_data_types/02_floats/float_vars/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "float_vars" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/code/02_data_types/02_floats/float_vars/Cargo.toml b/code/02_data_types/02_floats/float_vars/Cargo.toml new file mode 100644 index 0000000..f018640 --- /dev/null +++ b/code/02_data_types/02_floats/float_vars/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "float_vars" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "*" \ No newline at end of file diff --git a/code/02_data_types/02_floats/float_vars/src/main b/code/02_data_types/02_floats/float_vars/src/main new file mode 100755 index 0000000..c625529 Binary files /dev/null and b/code/02_data_types/02_floats/float_vars/src/main differ diff --git a/code/02_data_types/02_floats/float_vars/src/main.rs b/code/02_data_types/02_floats/float_vars/src/main.rs new file mode 100644 index 0000000..92e5efc --- /dev/null +++ b/code/02_data_types/02_floats/float_vars/src/main.rs @@ -0,0 +1,81 @@ +mod random_floats; +use random_floats::generate_random_float_f32; +use random_floats::generate_random_float_f64; + +fn main() { + make_separator(); + // invoke float_vars() function + float_vars(); + make_separator(); + + // Check the size + float_vars_size(); + make_separator(); + + // Generate random floats + random_float_vars(); + make_separator(); + + //format floats + format_float_vars(); + make_separator(); +} + +fn float_vars() { + // Rust has single-precision and double-precision floats. + // Create an f32 float variable + let f1: f32 = 1.23456; + + // Create an f64 float variable + let f2: f64 = 9.87654; + + println!("The 32 float variable is: {}", f1); + println!("The 64 float variable is: {}", f2); +} + +// A function that checks the size of float variables + +fn float_vars_size() { + // Check the size of float variables + let size_f32 = std::mem::size_of::(); + let size_f64 = std::mem::size_of::(); + + println!("The 32 float variable size is: {} bytes", size_f32); + println!("The 64 float variable size is: {} bytes", size_f64); +} + + +fn random_float_vars(){ + // Personally, I love to generate variables randomly + // Generate random float variables + + let rnd_fl_32 = generate_random_float_f32(0, 0.0, 9.9); + let rnd_fl_64 = generate_random_float_f64(0,0.0, 9.9); + + println!("Random float 32: {}", rnd_fl_32); + println!("Random float 64: {}", rnd_fl_64); + //println!("Floats to 2dp are {:.2} {:.2}", f1, f2); +} + +/* -------------------------------------------------------------- + Formatting Float variables + --------------------------------------------------------------*/ +fn format_float_vars() { + + // Create two float variables + let fl1: f32 = generate_random_float_f32(0, 1.0, 99.9); + let fl2: f64 = generate_random_float_f64(0, 1.0, 99.9); + + println!("A 25-width float vars L-aligned filled with spaces are:\n*|{:<25}|* and\n*|{:<25}|*", fl1, fl2); + make_separator(); + println!("A 25-width float vars R-aligned filled with space are:\n*|{:>25}|* and\n*|{:>25}|*", fl1, fl2); + make_separator(); + println!("A 25-width float vars L-aligned filled with tilde are:\n*|{:~<25}|* and\n*|{:~<25}|*", fl1, fl2); + make_separator(); + println!("A 25-width float vars R-aligned filled with tilde are:\n*|{:~>25}|* and\n*|{:~>25}|*", fl1, fl2); +} + +// Simple function that prints `=` to pretify the console output +fn make_separator() { + println!("{}", "=".repeat(52)); +} \ No newline at end of file diff --git a/code/02_data_types/02_floats/float_vars/src/random_floats.rs b/code/02_data_types/02_floats/float_vars/src/random_floats.rs new file mode 100644 index 0000000..f91ebdb --- /dev/null +++ b/code/02_data_types/02_floats/float_vars/src/random_floats.rs @@ -0,0 +1,36 @@ +use rand::SeedableRng; +use rand::distributions::{Distribution, Uniform}; +/// Generates a random single-precision (f32) float number within the specified range. +/// +/// # Arguments +/// +/// * `seed` - The seed value for the random number generator. +/// * `min` - The minimum value of the range (inclusive). +/// * `max` - The maximum value of the range (inclusive). +/// +/// # Returns +/// +/// Returns a random f32 float number within the specified range. +/// +pub fn generate_random_float_f32(seed: u64, min: f32, max: f32) -> f32 { + let mut rng = rand::rngs::StdRng::seed_from_u64(seed); + let range = Uniform::new_inclusive(min, max); + range.sample(&mut rng) +} +/// Generates a random double-precision (f64) float number within the specified range. +/// +/// # Arguments +/// +/// * `seed` - The seed value for the random number generator. +/// * `min` - The minimum value of the range (inclusive). +/// * `max` - The maximum value of the range (inclusive). +/// +/// # Returns +/// +/// Returns a random f64 float number within the specified range. + +pub fn generate_random_float_f64(seed: u64, min: f64, max: f64) -> f64 { + let mut rng = rand::rngs::StdRng::seed_from_u64(seed); + let range = Uniform::new_inclusive(min, max); + range.sample(&mut rng) +} diff --git a/code/02_data_types/03_booleans/bool_vars/Cargo.toml b/code/02_data_types/03_booleans/bool_vars/Cargo.toml new file mode 100644 index 0000000..242d687 --- /dev/null +++ b/code/02_data_types/03_booleans/bool_vars/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "bool_vars" +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/code/02_data_types/03_booleans/bool_vars/src/main b/code/02_data_types/03_booleans/bool_vars/src/main new file mode 100755 index 0000000..68e2537 Binary files /dev/null and b/code/02_data_types/03_booleans/bool_vars/src/main differ diff --git a/code/02_data_types/03_booleans/bool_vars/src/main.rs b/code/02_data_types/03_booleans/bool_vars/src/main.rs new file mode 100644 index 0000000..1dad719 --- /dev/null +++ b/code/02_data_types/03_booleans/bool_vars/src/main.rs @@ -0,0 +1,126 @@ +mod practice; +use practice::practice_with_bools; + +fn main() { + make_separator(); + boolean_vars(); + + make_separator(); + is_programming_fun(); + + make_separator(); + bool_vars_size(); + + make_separator(); + convert_bool_vars(); + + make_separator(); + logical_and(); + + make_separator(); + logical_or(); + + make_separator(); + logical_not(); + + // Check the result on the console + // then read the source code (writing functions will be in a separate chapter) + make_separator(); + practice_with_bools(); + + make_separator() +} + +// A function that checks boolean variables +fn boolean_vars() { + let is_ok: bool = true; // lowercase true + let is_bad: bool = false; // lowercase false + + println!( + "The boolean variables in Rust are: {} and {}", + is_ok, is_bad + ); +} + +// Example +fn is_programming_fun() { + let is_rust_fun: bool = true; + let _is_python_fun: bool = false; + + if is_rust_fun { + println!("Rust is fun!"); + } else { + println!("Python is fun!"); + } +} + +// Size of bool type + +fn bool_vars_size() { + let x: bool = true; + let y: bool = false; + println!( + "The size of {} is {} bytes.", + x, + std::mem::size_of::() + ); + println!( + "The size of {} is {} bytes.", + y, + std::mem::size_of::() + ); +} + +// Converting Booleans +// Boolean can be converted to integers using the `as` keyword +// true will be converted to 1 +// false will be converted to 0 + +fn convert_bool_vars() { + let x: bool = true; + let y: bool = false; + + let x_to_one: i32 = x as i32; // The as keyword performs the conversion + let y_to_zero: i32 = y as i32; + + println! {"The conversion of {} is {}", x, x_to_one}; + println! {"The conversion of {} is {}", y, y_to_zero}; +} + +// Logical Operators: +// 1. &&: And operator +// 2. ||: Or operator + +// Function to perform a logical AND operation +fn logical_and() { + let a: bool = true; + let b: bool = false; + println!("{:<6} and {:<5} is ==> {}", a, a, a && a); + println!("{:<6} and {:<5} is ==> {}", a, b, a && b); + println!("{:<6} and {:<5} is ==> {}", b, b, b && b); +} + +// Function to perform a logical OR operation +fn logical_or() { + let a: bool = true; + let b: bool = false; + println!("{:<6} or {:<5} is ==> {}", a, a, a || a); + println!("{:<6} or {:<5} is ==> {}", a, b, a || b); + println!("{:<6} or {:<5} is ==> {}", b, b, b || b); +} + +// The logical not `!` + +fn logical_not() { + let a: bool = true; + let b: bool = false; + println!("a is {:<6} and not a is {:<5}", a, !a); + println!("b is {:<6} and not b is {:<5}", b, !b); + println!("a and b is {:<10} and not (a and b) is ==>{}", a && b, !(a &&b)); + println!("a or b is {:<10} and not (a or b) is ==>{}", a || b, !(a ||b)); +} + +// Simple function for formatting the output in the console +fn make_separator() { + println!("{}", "*".repeat(52)); +} diff --git a/code/02_data_types/03_booleans/bool_vars/src/practice.rs b/code/02_data_types/03_booleans/bool_vars/src/practice.rs new file mode 100644 index 0000000..6524bdf --- /dev/null +++ b/code/02_data_types/03_booleans/bool_vars/src/practice.rs @@ -0,0 +1,53 @@ + +// Function to check if a number is even + +pub fn is_even(number: i32) -> bool { + number % 2 == 0 +} + +// Function to check if a number is prime +pub fn is_prime(number: u32) -> bool { + if number <= 1 { + return false; + } + for i in 2..(number / 2 + 1) { + if number % i == 0 { + return false; + } + } + true +} +// Logical operators + +// Function to perform a logical AND operation +fn logical_and(a: bool, b: bool) -> bool { + a && b +} + +// Function to perform a logical OR operation +fn logical_or(a: bool, b: bool) -> bool { + a || b +} +pub fn practice_with_bools() { + let numbers = 1..=7; // Create a range of values from 1 to 9 (inclusive). + + for number in numbers { + let result = is_even(number); + println!("Is {} even? {}", number, result); + } + println!("{}", "*".repeat(52)); + let numbers_2 = (1..=17).step_by(2); + for number in numbers_2 { + let is_prime_num = is_prime(number); + println!("Is {} a prime number? {}", number, is_prime_num); + } + println!("{}", "*".repeat(52)); + let a = true; + let b = false; + let and_result = logical_and(a, b); + println!("Logical AND: {:<10} && {:<10} = {}", a, b, and_result); + + let or_result = logical_or(a, b); + println!("Logical OR : {:<10} || {:<10} = {}", a, b, or_result); +} + diff --git a/code/02_data_types/04_chars/char_vars/Cargo.toml b/code/02_data_types/04_chars/char_vars/Cargo.toml new file mode 100644 index 0000000..d270949 --- /dev/null +++ b/code/02_data_types/04_chars/char_vars/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "char_vars" +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/code/02_data_types/04_chars/char_vars/src/main b/code/02_data_types/04_chars/char_vars/src/main new file mode 100755 index 0000000..c4fb881 Binary files /dev/null and b/code/02_data_types/04_chars/char_vars/src/main differ diff --git a/code/02_data_types/04_chars/char_vars/src/main.rs b/code/02_data_types/04_chars/char_vars/src/main.rs new file mode 100644 index 0000000..edef446 --- /dev/null +++ b/code/02_data_types/04_chars/char_vars/src/main.rs @@ -0,0 +1,83 @@ +mod practice; +use practice::is_vowel; +use practice::char_to_ascii; +use practice::is_digit_or_letter; + +fn main() { + make_separator(); + char_vars(); + + make_separator(); + char_vars_size(); + + make_separator(); + convert_char_vars(); + + // practice functions + // You are not expected to understand the source code of these functions. + make_separator(); + is_vowel(); + + make_separator(); + char_to_ascii(); + + make_separator(); + is_digit_or_letter(); + make_separator(); +} + +// A function that checks char variables +fn char_vars() { + let first_char: char = 'A'; + let second_char: char = 'Z'; + let emoji_char: char = '📚'; // Unicode character + let non_ascii_char: char = '⾀'; // Unicode character + + println!( + "Char variables in Rust: {}, {}, {}, {}", + first_char, second_char, emoji_char, non_ascii_char + ); +} + +// Size of char type variables +fn char_vars_size() { + let c: char = 'X'; + let emoji_char: char = '📚'; + let non_ascii_char: char = '⾀'; + let german_char: char = 'ß'; + + println!( + "The char type size is bytes is {}.", + std::mem::size_of::() + ); + println!( + "The size of a char {} is {} bytes.", + c, c.to_string().as_bytes().len() + ); + println!( + "The size of {} is {} bytes.", + german_char, german_char.to_string().as_bytes().len() + ); + println!( + "The size of {} is {} bytes.", + non_ascii_char, non_ascii_char.to_string().as_bytes().len() + ); + println!( + "The size of {} is {} bytes.", + emoji_char, + emoji_char.to_string().as_bytes().len() + ) +} + +// Convert char variables to integer +fn convert_char_vars() { + let char_value: char = '5'; + let int_value: i32 = char_value.to_digit(10).unwrap_or(0) as i32; + + println!("The conversion of {} to an integer is: {}", char_value, int_value); +} + +// Simple function for formatting the output in the console +fn make_separator() { + println!("{}", "*".repeat(52)); +} diff --git a/code/02_data_types/04_chars/char_vars/src/practice.rs b/code/02_data_types/04_chars/char_vars/src/practice.rs new file mode 100644 index 0000000..a32fbbb --- /dev/null +++ b/code/02_data_types/04_chars/char_vars/src/practice.rs @@ -0,0 +1,40 @@ +// Example: Check if a char is a vowel +pub fn is_vowel() { + let characters = ['E', 'H', 'i', 'o', 'U', 'Y']; + + for &character in &characters { + match character { + 'A' | 'E' | 'I' | 'O' | 'U' | 'a' | 'e' | 'i' | 'o' | 'u' => { + println!("{} is a vowel.", character); + } + _ => { + println!("{} is not a vowel.", character); + } + } + } +} + + +// Convert a char to its ASCII value +pub fn char_to_ascii() { + let characters = ['A', 'a', 'R', 'u', 'S', 'T', 'P']; + + for &char_value in &characters { + let ascii_value: u8 = char_value as u8; + println!("The ASCII value of {} is: {}", char_value, ascii_value); + } +} + +// Check whether a char is a digit or a letter +pub fn is_digit_or_letter() { + let characters = ['7', 'A', '0', '⍵']; + for &char_value in &characters { + if char_value.is_digit(10) { + println!("{} is a digit.", char_value); + } else if char_value.is_alphabetic() { + println!("{} is a letter.", char_value); + } else { + println!("{} is neither a digit nor a letter.", char_value); + } + } +} diff --git a/code/02_data_types/05_mutability/mutability/Cargo.toml b/code/02_data_types/05_mutability/mutability/Cargo.toml new file mode 100644 index 0000000..b0efabb --- /dev/null +++ b/code/02_data_types/05_mutability/mutability/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "mutability" +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/code/02_data_types/05_mutability/mutability/src/main b/code/02_data_types/05_mutability/mutability/src/main new file mode 100755 index 0000000..78c06cd Binary files /dev/null and b/code/02_data_types/05_mutability/mutability/src/main differ diff --git a/code/02_data_types/05_mutability/mutability/src/main.rs b/code/02_data_types/05_mutability/mutability/src/main.rs new file mode 100644 index 0000000..cc9ce42 --- /dev/null +++ b/code/02_data_types/05_mutability/mutability/src/main.rs @@ -0,0 +1,64 @@ +fn main() { + make_separator(); + immutable_vars(); + + make_separator(); + mutable_vars(); + + make_separator(); + compute_sum_for(); + + make_separator(); + compute_product_while(); + + make_separator() +} + +// Immutable variables +// Variables are immutable by default. + +fn immutable_vars(){ + + let imm_var = 0; + //imm_var = 1; + println!("The immutable variable is {}", imm_var); +} + +// Mutable variables +// You must use the mut qualifier to make a variable mutable. + +fn mutable_vars (){ + let mut var = 0; + println!("e originally is {}", var); + var = 1; + println!("e afterwards is {}", var); +} + +// Mutable variables are used in loops such as `for` or `while` +fn compute_sum_for() { + let mut total = 0; + let values = vec![1, 2, 3, 4, 5]; + + for x in values { + total += x; + } + println!("Sum of values: {}", total); +} + +fn compute_product_while() { + let mut total = 1; + let values = vec![1, 2, 3, 4, 5]; + let mut iter = values.iter(); + + while let Some(x) = iter.next() { + total *= *x; + } + + println!("Product of values: {}", total); +} + + +// Simple function for formatting the output in the console +fn make_separator() { + println!("{}", "*".repeat(52)); +} \ No newline at end of file diff --git a/code/02_data_types/06_constants/constants/Cargo.toml b/code/02_data_types/06_constants/constants/Cargo.toml new file mode 100644 index 0000000..06d8173 --- /dev/null +++ b/code/02_data_types/06_constants/constants/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "constants" +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/code/02_data_types/06_constants/constants/src/main b/code/02_data_types/06_constants/constants/src/main new file mode 100755 index 0000000..870812f Binary files /dev/null and b/code/02_data_types/06_constants/constants/src/main differ diff --git a/code/02_data_types/06_constants/constants/src/main.rs b/code/02_data_types/06_constants/constants/src/main.rs new file mode 100644 index 0000000..305924f --- /dev/null +++ b/code/02_data_types/06_constants/constants/src/main.rs @@ -0,0 +1,41 @@ +fn main() { + make_separator(); + const_vars(); + + make_separator(); + display_physical_constants(); + + make_separator() +} + +/* In Rust, it's a convention to use uppercase letters with underscores to name constant variables. +This convention helps distinguish constants from regular variables and makes the code more readable. While it's not a strict rule enforced by the Rust compiler, it's widely followed in the Rust community. +*/ + +fn const_vars() { + // You can define compile-time constants in Rust. + // Constants must have a specified type. + const SECONDS_IN_HOUR: i32 = 3_600; // The underscore is only for humans and + // does not have any special meaning to the compiler + const SECONDS_IN_DAY: i32 = 24 * SECONDS_IN_HOUR; + + // Constants are computed at compile time. + println!("There are {} seconds in a day", SECONDS_IN_DAY); +} + +// Example +fn display_physical_constants() { + + const SPEED_OF_LIGHT: f64 = 299_792_458.0; // meters per second + const GRAVITY: f64 = 9.81; // meters per second squared + const PLANCK_CONSTANT: f64 = 6.62607004e-34; // joule seconds + + println!("Speed of Light: {:.2} m/s", SPEED_OF_LIGHT); + println!("Acceleration due to Gravity: {:.2} m/s²", GRAVITY); + println!("Planck Constant: {:10e} J·s", PLANCK_CONSTANT); +} + +// Simple function for formatting the output in the console +fn make_separator() { + println!("{}", "*".repeat(52)); +} diff --git a/code/02_data_types/07_rustacean/be_rustacian/Cargo.toml b/code/02_data_types/07_rustacean/be_rustacian/Cargo.toml new file mode 100644 index 0000000..6a31970 --- /dev/null +++ b/code/02_data_types/07_rustacean/be_rustacian/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "be_rustacian" +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/code/02_data_types/07_rustacean/be_rustacian/src/main b/code/02_data_types/07_rustacean/be_rustacian/src/main new file mode 100755 index 0000000..054fdf1 Binary files /dev/null and b/code/02_data_types/07_rustacean/be_rustacian/src/main differ diff --git a/code/02_data_types/07_rustacean/be_rustacian/src/main.rs b/code/02_data_types/07_rustacean/be_rustacian/src/main.rs new file mode 100644 index 0000000..e2248c0 --- /dev/null +++ b/code/02_data_types/07_rustacean/be_rustacian/src/main.rs @@ -0,0 +1,96 @@ +use std::any::Any; + +fn main() { + make_separator(); + infer_var_type(); + + make_separator(); + print_var_type(); + + make_separator(); + unused_vars(); + + make_separator(); + cast_vars(); + + make_separator(); + redeclare_vars(); + + make_separator() +} + +fn infer_var_type() { + + // Rust lang can infer var types, so we don't need always to explicitly declare the var type. + let a = -12700; + let b = 2.71828; + let c = 'X'; + println!("a is {}, b is {}, c is {}", a, b, c); +} + +// Define a function that checks the variable type during runtime. + +fn check_type(value: &dyn Any) { + if value.is::() { + println!("The value is of type i32"); + } else if value.is::() { + println!("The value is of type bool"); + } else if value.is::() { + println!("The value is of type char"); + } else if value.is::() { + println!("The value is of type f64"); + } else { + println!("The value is of an unknown type"); + } +} + +// Use the previous function to check the type of variables. + +fn print_var_type(){ + let x = 32; + let y = true; + let z = 'A'; + let t = -234; + let f = 2.71828; + + check_type(&x); + check_type(&y); + check_type(&z); + check_type(&t); + check_type(&f); +} + +// Unused variables +// -------------------- +// If you don't use a variable, or you intend to use it in a later stage +// you can prefix name with _ to avoid compiler warning. + +fn unused_vars () { + let _f = 0; + let _is_ok = true; + println!("There some variables defined in this func, however, the compiler \ndoes not complain about not using them.") +} + + +// Casting +fn cast_vars(){ + // You can cast using the "as" keyword. + let g = 3.99; + let h = g as i32; + println!("g is {}, h is {}", g, h); +} + +// Redeclaring + +fn redeclare_vars (){ + // Rust enables you to redeclare a variable in the current scope. This is called shadowing and it's quite cool. + let num = String::from("1270101"); + println!("num {} is of type: `{}` with size of `{}` bytes.", num, std::any::type_name::(), num.to_string().as_bytes().len()); + let num = 1270101; + println!("num {} is of type: `{}` now with size of `{}` bytes", num , std::any::type_name::(), std::mem::size_of_val(&num)); +} + +// Simple function for formatting the output in the console +fn make_separator() { + println!("{}", "*".repeat(72)); +} \ No newline at end of file diff --git a/code/02_data_types/07_rustacean/script.md b/code/02_data_types/07_rustacean/script.md new file mode 100644 index 0000000..7a8b5d3 --- /dev/null +++ b/code/02_data_types/07_rustacean/script.md @@ -0,0 +1 @@ +So far in this lesson, we've seen how to declare variables. We've seen integers, floats, characters, and Booleans. And to wrap up the lesson, we're going to look at some additional techniques to help you write your code more like a Rust developer would. So some additional techniques which are quite useful, some of them are a bit surprising, but all of 'em interesting. So first of all, we have this concept of inferred types. When you declare a variable, if you give it a value, then you don't also need to specify the type. The compiler can guess the type based on the value that you provide. And this is the way that most Rust developers would do it. So here you can see I've declared A, B, and C. The compiler, I've given each one a value and the compiler can guess the types without me having to specify. It'll guess that a is an i32, I think, and it'll guess that b is a F64. I think that's what it would be. And C will be a character. So I don't actually need to say let a colon i32. I can just give it the value and it can deduce the type from that value. This is the way that most Rust developers write their code. You can also declare variables to be mutable or unmutable. Surprisingly, if you're coming from a C background or C Plus Plus or Java, in Rust, when you declare a variable like I've done here, by default it's immutable, okay, which means you can't change it. There's been a drift in languages in recent years towards this concept. Immutable variables are safer than ones that can change. If you know that a value is immutable, then for example in a multi-threaded application, you don't need to lock it. You can't change the value. So you don't need to lock it when you are accessing it because it's fixed. So Rust is really about safety and code integrity. That's the main selling point for Rust. So when you declare a variable, then it is by default immutable. You can't change the value. Once you've assigned it. That's it. If you do want to have a variable that's mutable then you have to use the mut keyword. Looks a bit odd, but you do get used to it. I declared e as a mutable integer. I've given that initial value of zero, but then I could change value later on okay? So you have to explicitly request mutability if you want it. If you don't say mut, then it's immutable by default for safety. Here's another curiosity. If you're writing some code, and you've declared a variable, but you haven't yet used it, you would normally get a compiler warning to say there's an unused variable here. Did you forget to write some code to use it? If it's intentional that you haven't yet written the code but you will, then you can prefix the variable name with an underscore, okay? And any variable that starts with an underscore, you won't get a warning from the compiler about unused variables okay? So there we go. That stops you getting lots of warnings about unused variables. Just prefix the name with an underscore. Okay that's quite straightforward enough. Now there's the as keyword. The as keyword is a type conversion. Rust doesn't do any type conversions on its own. If you want to convert an integer to a float or a float to an integer, you've gotta do that explicitly using a type conversion using the as keyword okay? So here in this example g is a floating point value and I want to basically take g and convert it into an i32, convert the value into an i32 and that integer value is then assigned to h. Okay so you have to use the as keyword to do any type conversions in Rust. It doesn't do any type conversions unless you say using the as keyword. And again, it's for code predictability and code safety. It only converts when you tell it to using the as keyword. Now this next one's a bit of a curiosity. Imagine you ask the user to enter a string, a number, let's say from the keyboard. They type in a number 1, 2, 3, 4, 5. It comes into your application as a string. You wanna convert it to an integer. So you might do, there's an pauseint function that we'll talk about later on in the course, but you can actually use the same variable name. So in this example initially I've got a variable called num, which is a string, and then maybe I convert it into a number but then I reassign it back to a variable with the same name. I've re-declared the num variable. It was a string initially, but from this point onwards, it'll be an integer. All right, so that's quite surprising. I think for most people, the fact that you can reuse a variable name, because in most other languages you couldn't. For example, in Java, if you declared the first variable as a string, you might declare a variable called num as a str, and give it a string value, 1, 2, 3, 4, 5, like that. And then you do some code to convert it to an integer. And then the integer value, you would then have to assign in other languages to a variable with a different name like num int. Okay, so initially you invented this name for the variable and then you invented this name for the second variable. And what Rust says is, or what you can say in Rust is you know, really it's the same concept. Initially, I'm going to express the number in string format but from here onwards, I want to express it as an integer. So from here onwards, it'll use the num as an integer. You don't have to invent different names for it. You can recycle the name and then use the new variable, the integer variable here afterwards, okay? You can still use it, the kind of old style approach if you like giving it variable names, which are kind of different or you can use the same variable name if you prefer as supported in Rust. Right one final thing, I'm gonna show you a demo after this. You can have compile-time constants. A value which is known at compile time doesn't need to be evaluated at runtime. So therefore slightly more efficient, because there's no runtime code involved. In this example, use the const keyword. Rust is quite fussy. When you have a const keyword, you basically have to use capital letters for the variable name. It's quite fussy about names. And you've also gotta specify the type of the variable as well. Okay you could argue that it can guess it can see that seconds in hour I've given it the value 3,600. It can see that it's an i32, but you have to explicitly tell it. Again, I think it's just for predictable code, so that it doesn't get the wrong idea. Okay and by the way the underscore here doesn't have any kind of real meaning. It's just there as a developer nicety to make it clearer, easier how to read the number 3,600. The underscore doesn't actually have any real meaning. It's just there for us programmers to read more easily. Let's have a look at an example. Same project as before, lesson two variables types. And it's the demo additional type function we're gonna look at. Then we'll run the example as usual. Okay, so here's my code, and in the main function I'm going to uncomment a call to that function. And here is the function down here. It's basically got bits of code similar to what we looked at in the slide, okay? So I'm gonna run through that in a moment. I'll run it first of all, so we can actually see the output. I think it'd be beneficial to be able to see the output first, and then we can kind of go through the code as we're discussing, cargo run. So it gave me a few warnings about unused functions, same as before and ignore those. And then we declared some variables of inferred type. So an integer, a float, and a character. I've got some new line characters being displayed as well here just to make my output look nice. So it outputs the value of A, B, and C. No great surprises there. Those are the values of A, B, and C as expected. And then here I've got a variable, which is immutable by default. If I try to change it, I'm gonna get a compiler error. Now, if I try to run the application, line 80 is gonna gimme an error. The warnings or the errors that you get in Rust are actually quite useful. It'll tell you there's an error on line 80, count the five. So here we are, here's line 80, but it tells you why the error occurred. It says, well, in the beginning you declared the variable and it wasn't mutable. Maybe it should have been because then you tried to reassign it, the value. Okay so it'll tell you what caused the error in blue, and then it tells you what the error actually is in red. So that's actually really useful. It tells you how to fix the error as well. The wording of the error message is quite useful. Quite suggestive, cannot assign twice to immutable variable. So when you declare an immutable variable, you don't have to initialize it all at once. You can declare it on one statement and then assign the value on the next statement. So that actually is okay. It's just that you can't assign it the value twice. So as it stands now, if I rebuild and rerun it, it'll actually be okay, I'm not gonna get any errors now. I declared it as immutable. You are allowed to assign it the value once somewhere, not necessarily immediately, but you're not allowed to then reassign it the value again. So if I then try to do this, okay, then the first assignment would be, okay, it's gotta have some value. The second assignment is the problem. So now I'm gonna get an error again. Okay, so we're kind of back to square one. So let me just revert my code to as it was, and I'll just kind of get rid of that statement. If you want to have a variable that is mutable, then you've gotta explicitly say mutable like so. Okay, so originally e will be zero. Afterwards, e will be one. Let's just verify what could possibly go wrong. Cargo run. Okay, so e was originally zero and afterwards e is one. Excellent what you might be tempted to do if you are familiar with C Plus Plus or almost any other language you might be tempted to say use the plus plus operator either as a postfix or as a prefix. Rust doesn't have a plus plus operator. It doesn't have a minus minus operator either. So I'm gonna get an error when I try to build and run. Plus plus is not a valid operator. It says prefix here. It's not valid as a postfix operator either. It suggests using the plus equals instead. That's quite helpful. So you could, if you wanted an increment by one, you could say e plus equals one, or you could obviously plus equals two, or you could multiply equals two. That would be the same as saying e equals e multiplied by two okay? So whatever you wanna do you can rotate it as much as you want now. Here I've got a variable f, which I might use later on, but I haven't used yet. The underscore will stop the compiler from giving me a warning. If I got rid of the underscore, I'd get a compiler warning and use local variable. Okay, and what else? Conversions. So g was 3.99. I convert the value 3.99 into an integer and then I output the value of h. So let's see, what is the value of g and what is the value of h? Let's give that a spin. So g is 3.99 and h is three. And again I think that's probably what you'd expect, isn't it when you take a fluid point number and convert it to an end. It doesn't round up, it just truncates, okay? It kind of goes downwards towards negative infinity. So 3.99 becomes three when you convert it into an integer as you can see down here. And then, so here's my num as a string. Okay, so num as a string is there, and then I've repurposed the num keyword. So that num is now an integer okay? So now I can do maths on it. Okay, now it's an integer. I can now treat it as an integer and I can do maths on it. I can add one. And then finally I've got a const, the number of seconds in an hour. you have to tell it as an integer 3,600. That's a compiled time constant. So is this, this is a compile time constant, even though I've done a bit of maths, it's all completely doable at compile time. So the compiler will effectively do the multiplication, and it'll tell me that the number of seconds in a day, if there are that many seconds in an hour, and there are 24 hours in a day, then the number of seconds in a day will be that number there okay? So the techniques that I've shown you in this section here are important. Rust developers tend to use type inference. Rust developers tend to declare variables as immutable wherever they can, and they only declare mutable where they have to. And you have to make an effort like that. Using underscores to avoid warnings. Most organizations have a rule that you're not allowed to run code that has warnings. So this would get rid of warnings. Type conversions. You do when you have to. It always feels a little bit unclean, but sometimes you do have to convert from one type to another using the as keyword. And then recycling variable names. You don't tend to do that much, but it's useful when you need to use it. And then compile time constants, obviously they give you readability. It's much better to have a compile time constant to represent a number like here, seconds in day. I could have hard coded that as being 86,400. But anybody reading my code will think what on earth is that number? Is that your daily salary? Sadly not. It's the number of seconds in a day, but using the name of the variable or the name of the constant makes my code clearer. Okay, so that wraps up this lesson. Good to go. diff --git a/code/02_data_types/bools/Cargo.toml b/code/02_data_types/bools/Cargo.toml deleted file mode 100644 index 5f3d2e5..0000000 --- a/code/02_data_types/bools/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "bools" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/code/02_data_types/bools/src/main.rs b/code/02_data_types/bools/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/code/02_data_types/bools/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/code/02_data_types/char_variables/Cargo.toml b/code/02_data_types/char_variables/Cargo.toml deleted file mode 100644 index 4553b84..0000000 --- a/code/02_data_types/char_variables/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "char_variables" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/code/02_data_types/char_variables/src/main.rs b/code/02_data_types/char_variables/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/code/02_data_types/char_variables/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/code/02_data_types/num_variables/Cargo.toml b/code/02_data_types/num_variables/Cargo.toml deleted file mode 100644 index 8931b71..0000000 --- a/code/02_data_types/num_variables/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "num_variables" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/code/02_data_types/num_variables/src/main.rs b/code/02_data_types/num_variables/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/code/02_data_types/num_variables/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/code/02_data_types/other_dtypes/Cargo.toml b/code/02_data_types/other_dtypes/Cargo.toml deleted file mode 100644 index cdd9291..0000000 --- a/code/02_data_types/other_dtypes/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "other_dtypes" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/code/02_data_types/other_dtypes/src/main.rs b/code/02_data_types/other_dtypes/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/code/02_data_types/other_dtypes/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}