diff --git a/Chapter01/arithmetic.rs b/Chapter01/__old/arithmetic.rs similarity index 100% rename from Chapter01/arithmetic.rs rename to Chapter01/__old/arithmetic.rs diff --git a/Chapter01/array.rs b/Chapter01/__old/array.rs similarity index 100% rename from Chapter01/array.rs rename to Chapter01/__old/array.rs diff --git a/Chapter01/__old/assignment.rs b/Chapter01/__old/assignment.rs new file mode 100644 index 0000000..3008192 --- /dev/null +++ b/Chapter01/__old/assignment.rs @@ -0,0 +1,31 @@ +use std::io::{self, Write}; +use std::f64; + +fn main() { + println!("Let's print some lines:"); + println!(); + println!("Hello, world!"); + println!("{}, {}!", "Hello", "world"); + println!("Arguments can be referred to by their position: {0}, {1}! and {1}, {0}! are built from the same arguments", "Hello", "world"); + + println!("Furthermore the arguments can be named: \"{greeting}, {object}!\"", greeting = "Hello", object = "World"); + + println!("Number formatting: Pi is {0:.3} or {0:.0} for short", f64::consts::PI); + + println!("... and there is more: {0:>0width$}={0:>width$}={0:#x}", 1535, width = 5); + print!("Printing without newlines ... "); + println!("is great"); + + let _ = write!(&mut io::stdout(), "Underneath, it's all writing to a stream..."); + println!(); + + println!("Write something!"); + let mut input = String::new(); + if let Ok(n) = io::stdin().read_line(&mut input) { + println!("You wrote: {} ({} bytes) ", input, n); + } + else { + eprintln!("There was an error :("); + } +} + diff --git a/Chapter01/boolean.rs b/Chapter01/__old/boolean.rs similarity index 100% rename from Chapter01/boolean.rs rename to Chapter01/__old/boolean.rs diff --git a/Chapter01/__old/calculator.rs b/Chapter01/__old/calculator.rs new file mode 100644 index 0000000..8b77552 --- /dev/null +++ b/Chapter01/__old/calculator.rs @@ -0,0 +1,16 @@ + +pub struct ArithmeticResults{ + sum: i32, + difference: i32, + product: i32, + quotient: f32, +} + +pub fn print_basic_arithmetics(a: i32, b: i32) -> ArithmeticResults { + ArithmeticResults { + sum: a + b, + difference: a - b, + product: a * b, + quotient: a / b + } +} \ No newline at end of file diff --git a/Chapter01/decimal.rs b/Chapter01/__old/decimal.rs similarity index 99% rename from Chapter01/decimal.rs rename to Chapter01/__old/decimal.rs index c8ada92..c8faed1 100644 --- a/Chapter01/decimal.rs +++ b/Chapter01/__old/decimal.rs @@ -15,4 +15,4 @@ fn main(){ // Shifts println!("{ten:>ws$}",ten=10, ws=5 ); println!("{ten:>0ws$}",ten=10, ws=5 ); -} +} diff --git a/Chapter01/__old/lib.rs b/Chapter01/__old/lib.rs new file mode 100644 index 0000000..f892636 --- /dev/null +++ b/Chapter01/__old/lib.rs @@ -0,0 +1,20 @@ +mod variables +mod mutablility +mod numbers +mod arithmetics +mod strings + +mod arrays +mod vectors +mod tuples + + + + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/Chapter01/mutuable.rs b/Chapter01/__old/mutuable.rs similarity index 100% rename from Chapter01/mutuable.rs rename to Chapter01/__old/mutuable.rs diff --git a/Chapter01/sample.rs b/Chapter01/__old/sample.rs similarity index 100% rename from Chapter01/sample.rs rename to Chapter01/__old/sample.rs diff --git a/Chapter01/string.rs b/Chapter01/__old/string.rs similarity index 100% rename from Chapter01/string.rs rename to Chapter01/__old/string.rs diff --git a/Chapter01/tuples.rs b/Chapter01/__old/tuples.rs similarity index 100% rename from Chapter01/tuples.rs rename to Chapter01/__old/tuples.rs diff --git a/Chapter01/vector.rs b/Chapter01/__old/vector.rs similarity index 100% rename from Chapter01/vector.rs rename to Chapter01/__old/vector.rs diff --git a/Chapter01/assignment b/Chapter01/assignment deleted file mode 100644 index a5d95de..0000000 Binary files a/Chapter01/assignment and /dev/null differ diff --git a/Chapter01/assignment.rs b/Chapter01/assignment.rs deleted file mode 100644 index 326ddb3..0000000 --- a/Chapter01/assignment.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Task : To explain assignment operations in rust -// Author : Vigneshwer -// Version : 1.0 -// Date : 3 Dec 2016 - -// Primitive libraries in rust -use std::{i8,i16,i32,i64,u8,u16,u32,u64,f32,f64,isize,usize}; -use std::io::stdin; - -fn main() { - println!("Understanding assignment"); - // Complier will automatically figure out the datatype if not mentioned - // Cannot change the value - let num =10; - println!("Num is {}", num); - - // immutuable can change the value - let age: i32 =40; - println!("Age is {}", age); - -} \ No newline at end of file diff --git a/Chapter01/boolean b/Chapter01/boolean deleted file mode 100644 index b83bc21..0000000 Binary files a/Chapter01/boolean and /dev/null differ diff --git a/Chapter01/calculator.rs b/Chapter01/calculator.rs deleted file mode 100644 index b3b6a64..0000000 --- a/Chapter01/calculator.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Task : Basic mathematical model on 2 numbers -// Date : 26th Dec 2016 -// Version : 1.0 -// Author : Vigneshwer - -// Libraries in rust -use std::io; -use std::{i32}; - -// Main Functions -fn main() { - - // Entering number 1 - println!("Enter First number ? "); - let mut input_1 = String::new(); - io::stdin().read_line(&mut input_1) - .expect("Failed to read line"); - - // Entering number 2 - println!("Enter second number ? "); - let mut input_2 = String::new(); - io::stdin().read_line(&mut input_2) - .expect("Failed to read line"); - - // Convert to int - let a_int: i32 = input_1.trim().parse() - .ok() - .expect("Please type a number!"); - - let b_int: i32 = input_2.trim().parse() - .ok() - .expect("Please type a number!"); - - // output of basic operations - println!("sum is: {}", a_int + b_int); - println!("difference is: {}", a_int - b_int); - println!("mutlipy is: {}", a_int * b_int); - println!("division is: {}", a_int / b_int); - -} \ No newline at end of file diff --git a/Chapter01/data-types/Cargo.toml b/Chapter01/data-types/Cargo.toml new file mode 100644 index 0000000..44699e7 --- /dev/null +++ b/Chapter01/data-types/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "data-types" +version = "0.1.0" +authors = ["Claus Matzinger "] +edition = "2018" + +[dependencies] diff --git a/Chapter01/data-types/src/lib.rs b/Chapter01/data-types/src/lib.rs new file mode 100644 index 0000000..2386f6b --- /dev/null +++ b/Chapter01/data-types/src/lib.rs @@ -0,0 +1,107 @@ + +// Rust allows another macro type: derive. It allows to "auto-implement" +// supported traits. Clone, Debug, Copy are typically handy to derive. +#[derive(Clone, Debug, Copy)] +struct MyCustomStruct { + a: i32, + b: u32, + pub c: f32 +} + +// A typical Rust struct has an impl block for behavior +impl MyCustomStruct { + + // The new function is static function, and by convention a constructor + pub fn new(a: i32, b: u32, c: f32) -> MyCustomStruct { + MyCustomStruct { + a: a, b: b, c: c + } + } + + // Instance functions feature a "self" reference as the first parameter + // This self reference can be mutable or owned, just like other variables + pub fn sum(&self) -> f32 { + self.a as f32 + self.b as f32 + self.c + } +} + + +#[cfg(test)] +mod tests { + use std::mem; + use super::MyCustomStruct; + + #[test] + fn test_custom_struct() { + // Rust features zero-overhead structs! + assert_eq!(mem::size_of::(), + mem::size_of::() + mem::size_of::() + mem::size_of::()); + + let m = MyCustomStruct::new(1, 2, 3_f32); + assert_eq!(m.a, 1); + assert_eq!(m.b, 2); + assert_eq!(m.c, 3_f32); + + // Let's call the instance method + assert_eq!(m.sum(), 6_f32); + + // The derived clone trait adds a method clone() and does a deep copy + let m2 = m.clone(); + // We use the Debug formatter to format the struct + assert_eq!(format!("{:?}", m2), "MyCustomStruct { a: 1, b: 2, c: 3.0 }"); + + // This is an implicit (deep) copy, possible only with the Copy trait + // Added mutability allows to change struct members + let mut m3 = m; + + // As a copy, this should not affect the other instances + m3.a = 100; + + // We'll make sure that the values didn't change anywhere else + assert_eq!(m2.a, 1); + assert_eq!(m.a, 1); + assert_eq!(m3.a, 100); + } + + #[test] + fn basic_math_stuff() { + // Works as expected + assert_eq!(2 + 2, 4); + + // Rust lets you specify the datatype on literals by appending + // them to the constant. Splitting them by _ is optional. + assert_eq!(3.14 + 22.86, 26_f32); + + // Some functions are only available on certain types + assert_eq!(2_i32.pow(2), 4); + assert_eq!(4_f32.sqrt(), 2_f32); + + // Rust features unsigned variations of integer types + let a: u64 = 32; + let b: u64 = 64; + + // Risky, this could overflow + assert_eq!(b - a, 32); + + // ... this is why there is an overflowing_sub() function available + assert_eq!(a.overflowing_sub(b), (18446744073709551584, true)); + + // By default, Rust variables are immutable, add the mut qualifier + // to be able to change the value + let mut c = 100; + c += 1; + assert_eq!(c, 101); + } + + #[test] + #[should_panic] + fn attempt_overflows() { + let a = 10_u32; + let b = 11_u32; + + // This will panic since the result is going to be an unsigned + // type which cannot handle negative numbers + // Note: _ means ignore the result + let _ = a - b; + } +} diff --git a/Chapter01/debug-me/.vscode/launch.json b/Chapter01/debug-me/.vscode/launch.json new file mode 100644 index 0000000..68af88d --- /dev/null +++ b/Chapter01/debug-me/.vscode/launch.json @@ -0,0 +1,43 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'debug-me'", + "cargo": { + "args": [ + "build", + "--bin=debug-me", + "--package=debug-me" + ], + "filter": { + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'debug-me'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=debug-me", + "--package=debug-me" + ], + "filter": { + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/Chapter01/debug-me/Cargo.toml b/Chapter01/debug-me/Cargo.toml new file mode 100644 index 0000000..5013408 --- /dev/null +++ b/Chapter01/debug-me/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "debug-me" +version = "0.1.0" +authors = ["Claus Matzinger "] +edition = "2018" + +[dependencies] diff --git a/Chapter01/debug-me/launch.json b/Chapter01/debug-me/launch.json new file mode 100644 index 0000000..80746de --- /dev/null +++ b/Chapter01/debug-me/launch.json @@ -0,0 +1,15 @@ +{ + "version": "0.2.0", + "configurations": [ + + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceRoot}/target/debug/${workspaceRootFolderName}", + "args": [], + "cwd": "${workspaceRoot}", + "sourceLanguages": ["rust"] + } + ] +} \ No newline at end of file diff --git a/Chapter01/debug-me/src/main.rs b/Chapter01/debug-me/src/main.rs new file mode 100644 index 0000000..793125e --- /dev/null +++ b/Chapter01/debug-me/src/main.rs @@ -0,0 +1,16 @@ +struct MyStruct { + prop: usize, +} + +struct Point(f32, f32); + +fn main() { + let a = 42; + let b = vec![0, 0, 0, 100]; + let c = [1, 2, 3, 4, 5]; + let d = 0x5ff; + let e = MyStruct { prop: 10 }; + let p = Point(3.14, 3.14); + + println!("Hello, world!"); +} diff --git a/Chapter01/execution-flow/Cargo.toml b/Chapter01/execution-flow/Cargo.toml new file mode 100644 index 0000000..e767da2 --- /dev/null +++ b/Chapter01/execution-flow/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "execution-flow" +version = "0.1.0" +authors = ["Claus Matzinger "] +edition = "2018" + +[dependencies] diff --git a/Chapter01/execution-flow/src/lib.rs b/Chapter01/execution-flow/src/lib.rs new file mode 100644 index 0000000..2d09f08 --- /dev/null +++ b/Chapter01/execution-flow/src/lib.rs @@ -0,0 +1,86 @@ +#[cfg(test)] +mod tests { + #[test] + fn conditionals() { + let i = 20; + // Rust's if statement does not require parenthesis + if i < 2 { + assert!(i < 2); + } else if i > 2 { + assert!(i > 2); + } else { + assert_eq!(i, 2); + } + } + + #[test] + fn loops() { + + let mut i = 42; + let mut broke = false; + + // a basic loop with control statements + loop { + i -= 1; + if i < 2 { + broke = true; + break; + } else if i > 2 { + continue; + } + } + assert!(broke); + + // loops and other constructs can be named for better readability ... + 'outer: loop { + 'inner: loop { + break 'inner; // ... and specifically jumped out of + } + break 'outer; + } + let mut iterations: u32 = 0; + + // loops can even have return values on breaks + let total_squared = loop { + iterations += 1; + + if iterations >= 10 { + break iterations.pow(2); + } + }; + assert_eq!(total_squared, 100); + + // for loops can use ranges ... + for i in 0..10 { + assert!(i >= 0 && i < 10) + } + + // or anything that is an iterator + for v in vec![1, 1, 1, 1].iter() { + assert_eq!(v, &1); + } + } + + #[test] + fn more_conditionals() { + let my_option = Some(10); + + // If let statements can do simple pattern matching + if let Some(unpacked) = my_option { + assert_eq!(unpacked, 10); + } + + let mut other_option = Some(2); + // there is also while let, which does the same thing + while let Some(unpacked) = other_option { + + // if can also return values in assignments + other_option = if unpacked > 0 { + Some(unpacked - 1) + } else { + None + } + } + assert_eq!(other_option, None) + } +} diff --git a/Chapter01/hello-world/.gitignore b/Chapter01/hello-world/.gitignore new file mode 100644 index 0000000..f0e3bca --- /dev/null +++ b/Chapter01/hello-world/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk \ No newline at end of file diff --git a/Chapter01/hello-world/Cargo.toml b/Chapter01/hello-world/Cargo.toml new file mode 100644 index 0000000..f785abe --- /dev/null +++ b/Chapter01/hello-world/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "hello-world" +version = "0.1.0" +authors = ["Claus Matzinger "] +edition = "2018" + +[dependencies] diff --git a/Chapter01/hello-world/src/main.rs b/Chapter01/hello-world/src/main.rs new file mode 100644 index 0000000..90eeab5 --- /dev/null +++ b/Chapter01/hello-world/src/main.rs @@ -0,0 +1,40 @@ +use std::io::{self, Write}; +use std::f64; + +fn main() { + // Basic printing with arguments + println!("Let's print some lines:"); + println!(); + println!("Hello, world!"); + println!("{}, {}!", "Hello", "world"); + // No newlines + print!("Hello, "); + println!("world!"); + + println!("Arguments can be referred to by their position: {0}, {1}! and {1}, {0}! are built from the same arguments", "Hello", "world"); + + // More complex arguments + println!("Furthermore the arguments can be named: \"{greeting}, {object}!\"", greeting = "Hello", object = "World"); + + // Number formatting + println!("Number formatting: Pi is {0:.3} or {0:.0} for short", f64::consts::PI); + + // Padding and hex formatting + println!("... and there is more: {0:>0width$}={0:>width$}={0:#x}", 1535, width = 5); + + // Writing to a stream directly + let _ = write!(&mut io::stdout(), "Underneath, it's all writing to a stream..."); + println!(); + + // Reading from std::in + println!("Write something!"); + let mut input = String::new(); + if let Ok(n) = io::stdin().read_line(&mut input) { + println!("You wrote: {} ({} bytes) ", input, n); + } + else { + // Printing to std::err + eprintln!("There was an error :("); + } +} + diff --git a/Chapter01/mutuable b/Chapter01/mutuable deleted file mode 100644 index 5c801e1..0000000 Binary files a/Chapter01/mutuable and /dev/null differ diff --git a/Chapter01/pi-estimator/Cargo.toml b/Chapter01/pi-estimator/Cargo.toml new file mode 100644 index 0000000..f820d12 --- /dev/null +++ b/Chapter01/pi-estimator/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "pi-estimator" +version = "0.1.0" +authors = ["Claus Matzinger "] +edition = "2018" + +[dependencies] +rust-pilib = { path = '../rust-pilib', version = '*'} \ No newline at end of file diff --git a/Chapter01/pi-estimator/src/main.rs b/Chapter01/pi-estimator/src/main.rs new file mode 100644 index 0000000..620cc2f --- /dev/null +++ b/Chapter01/pi-estimator/src/main.rs @@ -0,0 +1,26 @@ +// declare the module by its file name +mod rounding; + +// Rust will also accept if you implement it right away +mod printer { + // import a function from an external crate (no more extern declaration required!) + use rust_pilib::monte_carlo_pi; + + // crates present in the parent can be imported using the crate prefix + use crate::rounding::round; + + pub fn pretty_print_pi_approx(iterations: usize) { + let pi = monte_carlo_pi(iterations); + let places: usize = 2; + + println!("Pi is ~ {} and rounded to {} places {}", pi, places, round(pi, places)); + } +} + +// import from the module above +use printer::pretty_print_pi_approx; + + +fn main() { + pretty_print_pi_approx(100_000); +} diff --git a/Chapter01/pi-estimator/src/rounding/mod.rs b/Chapter01/pi-estimator/src/rounding/mod.rs new file mode 100644 index 0000000..df372b0 --- /dev/null +++ b/Chapter01/pi-estimator/src/rounding/mod.rs @@ -0,0 +1,30 @@ + +pub fn round(nr: f32, places: usize) -> f32 { + let multiplier = 10_f32.powi(places as i32); + (nr * multiplier + 0.5).floor() / multiplier +} + + +#[cfg(test)] +mod tests { + use super::round; + + #[test] + fn round_positive() { + assert_eq!(round(3.123456, 2), 3.12); + assert_eq!(round(3.123456, 4), 3.1235); + assert_eq!(round(3.999999, 2), 4.0); + assert_eq!(round(3.0, 2), 3.0); + assert_eq!(round(9.99999, 2), 10.0); + assert_eq!(round(0_f32, 2), 0_f32); + } + + #[test] + fn round_negative() { + assert_eq!(round(-3.123456, 2), -3.12); + assert_eq!(round(-3.123456, 4), -3.1235); + assert_eq!(round(-3.999999, 2), -4.0); + assert_eq!(round(-3.0, 2), -3.0); + assert_eq!(round(-9.99999, 2), -10.0); + } +} diff --git a/Chapter01/rust-pilib/Cargo.toml b/Chapter01/rust-pilib/Cargo.toml new file mode 100644 index 0000000..3735761 --- /dev/null +++ b/Chapter01/rust-pilib/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust-pilib" +version = "0.1.0" +authors = ["Claus Matzinger "] +edition = "2018" + +[dependencies] +rand = "^0.5" \ No newline at end of file diff --git a/Chapter01/rust-pilib/src/lib.rs b/Chapter01/rust-pilib/src/lib.rs new file mode 100644 index 0000000..f17b417 --- /dev/null +++ b/Chapter01/rust-pilib/src/lib.rs @@ -0,0 +1,54 @@ +use rand::prelude::*; + +pub fn monte_carlo_pi(iterations: usize) -> f32 { + let mut inside_circle = 0; + for _ in 0..iterations { + + // generate two random coordinates between 0 and 1 + let x: f32 = random::(); + let y: f32 = random::(); + + // calculate the circular distance from 0, 0 + if x.powi(2) + y.powi(2) <= 1_f32 { + // if it's within the circle, increase the count + inside_circle += 1; + } + } + // return the ratio of 4 times the hits to the total iterations + (4_f32 * inside_circle as f32) / iterations as f32 +} + +#[cfg(test)] +mod tests { + // import the parent crate's functions + + use super::*; + + fn is_reasonably_pi(pi: f32) -> bool { + pi >= 3_f32 && pi <= 4.5_f32 + } + + #[test] + fn test_monte_carlo_pi_1() { + let pi = monte_carlo_pi(1); + assert!(pi == 0_f32 || pi == 4_f32); + } + + #[test] + fn test_monte_carlo_pi_500() { + let pi = monte_carlo_pi(500); + assert!(is_reasonably_pi(pi)); + } + + #[test] + fn test_monte_carlo_pi_1000() { + let pi = monte_carlo_pi(1000); + assert!(is_reasonably_pi(pi)); + } + + #[test] + fn test_monte_carlo_pi_5000() { + let pi = monte_carlo_pi(5000); + assert!(is_reasonably_pi(pi)); + } +} diff --git a/Chapter01/sample b/Chapter01/sample deleted file mode 100644 index dfb7690..0000000 Binary files a/Chapter01/sample and /dev/null differ diff --git a/Chapter01/sequences/Cargo.toml b/Chapter01/sequences/Cargo.toml new file mode 100644 index 0000000..94ee8e1 --- /dev/null +++ b/Chapter01/sequences/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "sequences" +version = "0.1.0" +authors = ["Claus Matzinger "] +edition = "2018" + +[dependencies] diff --git a/Chapter01/sequences/src/lib.rs b/Chapter01/sequences/src/lib.rs new file mode 100644 index 0000000..529742b --- /dev/null +++ b/Chapter01/sequences/src/lib.rs @@ -0,0 +1,89 @@ +#[cfg(test)] +mod tests { + use std::mem; + + #[test] + fn exploring_vec() { + // a Vec is almost always initialized using a macro + assert_eq!(vec![0; 3], [0, 0, 0]); + let mut v: Vec = vec![]; + + // a Vec is defined by a triple (pointer, capacity, length) + assert_eq!(mem::size_of::>(), mem::size_of::() * 3); + + // empty vectors point to no memory (yet) + assert_eq!(mem::size_of_val(&*v), 0); + + v.push(10); + + // a vector will also over-allocate on insert + // *by how much is an implementation detail and may change!* + assert_eq!(mem::size_of::>(), mem::size_of::() * 6); + + // vectors support indexing + assert_eq!(v[0], 10); + + // vectors have some convenience methods + v.insert(0, 11); + v.push(12); + assert_eq!(v, [11, 10, 12]); + assert!(!v.is_empty()); + + // ... like one to create a heap in only a few lines + assert_eq!(v.swap_remove(0), 11); + assert_eq!(v, [12, 10]); + + // ... or a stack + assert_eq!(v.pop(), Some(10)); + assert_eq!(v, [12]); + + // vectors also support regular removals + assert_eq!(v.remove(0), 12); + + // and can go back to occupying no memory.. + v.shrink_to_fit(); + assert_eq!(mem::size_of_val(&*v), 0); + } + + struct Point(f32, f32); + + #[test] + fn exploring_tuples() { + let mut my_tuple: (i32, usize, f32) = (10, 0, -3.42); + + // members can be accessed like that + assert_eq!(my_tuple.0, 10); + assert_eq!(my_tuple.1, 0); + assert_eq!(my_tuple.2, -3.42); + + my_tuple.0 = 100; + assert_eq!(my_tuple.0, 100); + + + + // tuples can be unpacked + let (_val1, _val2, _val3) = my_tuple; + + // structs can be based on tuples too + let point = Point(1.2, 2.1); + assert_eq!(point.0, 1.2); + assert_eq!(point.1, 2.1); + } + + + #[test] + fn exploring_arrays() { + // arrays use a familiar signature + // (type declarations are not necessary) + let mut arr: [usize; 3] = [0; 3]; + assert_eq!(arr, [0, 0, 0]); + + let arr2: [usize; 5] = [1,2,3,4,5]; + assert_eq!(arr2, [1,2,3,4,5]); + + arr[0] = 1; + assert_eq!(arr, [1, 0, 0]); + assert_eq!(arr[0], 1); + assert_eq!(mem::size_of_val(&arr), mem::size_of::() * 3); + } +} diff --git a/Chapter01/string b/Chapter01/string deleted file mode 100644 index 7474a4b..0000000 Binary files a/Chapter01/string and /dev/null differ diff --git a/Chapter01/testing/Cargo.toml b/Chapter01/testing/Cargo.toml new file mode 100644 index 0000000..b1d0cc8 --- /dev/null +++ b/Chapter01/testing/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "testing" +version = "0.1.0" +authors = ["Claus Matzinger "] +edition = "2018" + +[dependencies] diff --git a/Chapter01/testing/src/lib.rs b/Chapter01/testing/src/lib.rs new file mode 100644 index 0000000..b20c0c1 --- /dev/null +++ b/Chapter01/testing/src/lib.rs @@ -0,0 +1,272 @@ +//! +//! A simple singly-linked list for the Rust-Cookbook by Packt Publishing. +//! +//! Recipes covered in this module: +//! - Documenting your code +//! - Testing your documentation +//! - Writing tests and benchmarks +//! + + +#![feature(test)] +#![doc(html_logo_url = "https://blog.x5ff.xyz/img/main/logo.png", + test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] + +use std::cell::RefCell; +use std::rc::Rc; + +type Link = Option>>>; + +#[derive(Clone)] +struct Node where T: Sized + Clone { + value: T, + next: Link, +} + + +impl Node where T: Sized + Clone { + fn new(value: T) -> Rc>> { + Rc::new(RefCell::new(Node { + value: value, + next: None, + })) + } +} + +/// +/// A singly-linked list, with nodes allocated on the heap using `Rc`s and `RefCell`s. Here's an image illustrating a linked list: +/// +/// +/// ![](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg) +/// +/// *Found on https://en.wikipedia.org/wiki/Linked_list* +/// +/// # Usage +/// +/// ```ignore +/// let list = List::new_empty(); +/// ``` +/// +#[derive(Clone)] +pub struct List where T: Sized + Clone { + head: Link, + tail: Link, + + /// + /// The length of the list. + /// + pub length: usize, +} + +impl List where T: Sized + Clone { + + /// + /// Creates a new empty list. + /// + /// + /// # Example + /// + /// ``` + /// # use testing::List; + /// let list: List = List::new_empty(); + /// ``` + /// + pub fn new_empty() -> List { + List { head: None, tail: None, length: 0 } + } + + /// + /// Appends a node to the list at the end. + /// + /// + /// # Panics + /// + /// This never panics (probably). + /// + /// # Safety + /// + /// No unsafe code was used. + /// + /// # Example + /// + /// ``` + /// use testing::List; + /// + /// let mut list = List::new_empty(); + /// list.append(10); + /// ``` + /// + pub fn append(&mut self, value: T) { + let new = Node::new(value); + match self.tail.take() { + Some(old) => old.borrow_mut().next = Some(new.clone()), + None => self.head = Some(new.clone()) + }; + self.length += 1; + self.tail = Some(new); + } + + /// + /// Removes the list's head and returns the result. + /// + /// + /// # Panics + /// + /// Whenever when a node unexpectedly is `None` + /// + /// # Example + /// + /// ``` + /// # use testing::List; + /// + /// let mut list = List::new_empty(); + /// list.append(10); + /// assert_eq!(list.pop(), Some(10)); + /// ``` + /// + pub fn pop(&mut self) -> Option { + self.head.take().map(|head| { + if let Some(next) = head.borrow_mut().next.take() { + self.head = Some(next); + } else { + self.tail.take(); + } + self.length -= 1; + Rc::try_unwrap(head) + .ok() + .expect("Something is terribly wrong") + .into_inner() + .value + }) + } + + + /// + /// Splits off and returns `n` nodes as a `List`. + /// + /// # Arguments + /// + /// `n: usize` - The number of elements after which to split the list. + /// + /// # Panics + /// + /// Panics when: + /// - The list is empty + /// - `n` is larger than the length + /// + /// # Example + /// + /// ``` + /// # use testing::List; + /// + /// let mut list = List::new_empty(); + /// list.append(12); + /// list.append(11); + /// list.append(10); + /// let mut list2 = list.split(1); + /// assert_eq!(list2.pop(), Some(12)); + /// assert_eq!(list.pop(), Some(11)); + /// assert_eq!(list.pop(), Some(10)); + /// ``` + /// + pub fn split(&mut self, n: usize) -> List { + + // Don't do this in real life. Use Results, Options, or anything that + // doesn't just kill the program + if self.length == 0 || n >= self.length - 1 { + panic!("That's not working"); + } + + let mut n = n; + let mut new_list = List::new_empty(); + while n > 0 { + new_list.append(self.pop().unwrap()); + n -= 1; + } + new_list + } +} + +impl Drop for List where T: Clone + Sized { + + fn drop(&mut self) { + while self.length > 0 { + let n = self.pop(); + drop(n); + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + extern crate test; + use test::Bencher; + + #[bench] + fn bench_list_append(b: &mut Bencher) { + let mut list = List::new_empty(); + b.iter(|| { + list.append(10); + }); + } + + #[test] + fn test_list_new_empty() { + let mut list: List = List::new_empty(); + assert_eq!(list.length, 0); + assert_eq!(list.pop(), None); + } + + #[test] + fn test_list_append() { + let mut list = List::new_empty(); + list.append(1); + list.append(1); + list.append(1); + list.append(1); + list.append(1); + assert_eq!(list.length, 5); + } + + + #[test] + fn test_list_pop() { + let mut list = List::new_empty(); + list.append(1); + list.append(1); + list.append(1); + list.append(1); + list.append(1); + assert_eq!(list.length, 5); + assert_eq!(list.pop(), Some(1)); + assert_eq!(list.pop(), Some(1)); + assert_eq!(list.pop(), Some(1)); + assert_eq!(list.pop(), Some(1)); + assert_eq!(list.pop(), Some(1)); + assert_eq!(list.length, 0); + assert_eq!(list.pop(), None); + } + + #[test] + fn test_list_split() { + let mut list = List::new_empty(); + list.append(1); + list.append(1); + list.append(1); + list.append(1); + list.append(1); + assert_eq!(list.length, 5); + let list2 = list.split(3); + assert_eq!(list.length, 2); + assert_eq!(list2.length, 3); + } + + #[test] + #[should_panic] + fn test_list_split_panics() { + let mut list: List = List::new_empty(); + let _ = list.split(3); + } +} diff --git a/Chapter01/testing/tests/list_integration.rs b/Chapter01/testing/tests/list_integration.rs new file mode 100644 index 0000000..3d0060a --- /dev/null +++ b/Chapter01/testing/tests/list_integration.rs @@ -0,0 +1,11 @@ +use testing::List; + + +#[test] +fn test_list_insert_10k_items() { + let mut list = List::new_empty(); + for _ in 0..10_000 { + list.append(100); + } + assert_eq!(list.length, 10_000); +} \ No newline at end of file diff --git a/Chapter01/traits/Cargo.toml b/Chapter01/traits/Cargo.toml new file mode 100644 index 0000000..1514dc2 --- /dev/null +++ b/Chapter01/traits/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "traits" +version = "0.1.0" +authors = ["Claus Matzinger "] +edition = "2018" + +[dependencies] diff --git a/Chapter01/traits/src/lib.rs b/Chapter01/traits/src/lib.rs new file mode 100644 index 0000000..b35bca1 --- /dev/null +++ b/Chapter01/traits/src/lib.rs @@ -0,0 +1,140 @@ +use std::io::{Write, Read}; +// Structs + +/// +/// Configuration for our application +/// +pub struct Config { + values: Vec<(String, String)> +} + +/// +/// A service for managing a configuration +/// +pub struct KeyValueConfigService { + +} + +// Traits + +/// +/// Provides a get() function to return valuse associated with +/// the specified key. +/// +pub trait ValueGetter { + fn get(&self, s: &str) -> Option; +} + +/// +/// Write a config +/// +pub trait ConfigWriter { + fn write(&self, config: Config, to: &mut impl Write) -> std::io::Result<()>; +} + +/// +/// Read a config +/// +pub trait ConfigReader { + fn read(&self, from: &mut impl Read) -> std::io::Result; +} + +// Impls + +impl Config { + pub fn new(values: Vec<(String, String)>) -> Config { + Config { + values: values + } + } +} + +impl KeyValueConfigService { + pub fn new() -> KeyValueConfigService { + KeyValueConfigService { + } + } +} + +impl ConfigWriter for KeyValueConfigService { + fn write(&self, config: Config, mut to: &mut impl Write) -> std::io::Result<()> { + for v in config.values { + writeln!(&mut to, "{0}={1}", v.0, v.1)?; + } + Ok(()) + } +} + +impl ConfigReader for KeyValueConfigService { + fn read(&self, from: &mut impl Read) -> std::io::Result { + let mut buffer = String::new(); + from.read_to_string(&mut buffer)?; + + // chain iterators together and collect the results + let values: Vec<(String, String)> = buffer + .split_terminator("\n") // split + .map(|line| line.trim()) // remove whitespace + .filter(|line| { // filter invalid lines + let pos = line.find("=") + .unwrap_or(0); + pos > 0 && pos < line.len() - 1 + }) + .map(|line| { // create a tuple from a line + let parts = line.split("=").collect::>(); + (parts[0].to_string(), parts[1].to_string()) + }) + .collect(); // transform it into a vector + Ok(Config::new(values)) + } +} + +impl ValueGetter for Config { + fn get(&self, s: &str) -> Option { + self.values.iter() + .find_map(|tuple| if &tuple.0 == s { + Some(tuple.1.clone()) + } else { + None + }) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + + #[test] + fn config_get_value() { + let config = Config::new(vec![("hello".to_string(), "world".to_string())]); + assert_eq!(config.get("hello"), Some("world".to_string())); + assert_eq!(config.get("HELLO"), None); + } + + + #[test] + fn keyvalueconfigservice_write_config() { + let config = Config::new(vec![("hello".to_string(), "world".to_string())]); + + let service = KeyValueConfigService::new(); + let mut target = vec![]; + assert!(service.write(config, &mut target).is_ok()); + + assert_eq!(String::from_utf8(target).unwrap(), "hello=world\n".to_string()); + } + + #[test] + fn keyvalueconfigservice_read_config() { + + let service = KeyValueConfigService::new(); + let readable = &format!("{}\n{}", "hello=world", "a=b").into_bytes(); + + let config = service.read(&mut Cursor::new(readable)) + .expect("Couldn't read from the vector"); + + assert_eq!(config.values, vec![ + ("hello".to_string(), "world".to_string()), + ("a".to_string(), "b".to_string())]); + } +} \ No newline at end of file diff --git a/Chapter01/tuples b/Chapter01/tuples deleted file mode 100644 index ef3c955..0000000 Binary files a/Chapter01/tuples and /dev/null differ