From 88c3df4cf5ecc956c6c1db163f1f986918f0ddc9 Mon Sep 17 00:00:00 2001 From: "orestis.malaspin" <orestis.malaspinas@hesge.ch> Date: Tue, 4 Jul 2023 17:15:31 +0200 Subject: [PATCH] Adds part06 featuring modules and doc tests --- codes/rust_lang/part02/src/main.rs | 2 +- codes/rust_lang/part04/Cargo.toml | 8 + codes/rust_lang/part04/src/main.rs | 117 ++++++++++ codes/rust_lang/part05/Cargo.toml | 8 + codes/rust_lang/part05/src/main.rs | 213 ++++++++++++++++++ codes/rust_lang/part06/Cargo.toml | 8 + codes/rust_lang/part06/src/io.rs | 13 ++ codes/rust_lang/part06/src/lib.rs | 61 +++++ codes/rust_lang/part06/src/main.rs | 12 + codes/rust_lang/part06/src/minimum.rs | 15 ++ .../part06/src/something_or_nothing.rs | 84 +++++++ 11 files changed, 540 insertions(+), 1 deletion(-) create mode 100644 codes/rust_lang/part04/Cargo.toml create mode 100644 codes/rust_lang/part04/src/main.rs create mode 100644 codes/rust_lang/part05/Cargo.toml create mode 100644 codes/rust_lang/part05/src/main.rs create mode 100644 codes/rust_lang/part06/Cargo.toml create mode 100644 codes/rust_lang/part06/src/io.rs create mode 100644 codes/rust_lang/part06/src/lib.rs create mode 100644 codes/rust_lang/part06/src/main.rs create mode 100644 codes/rust_lang/part06/src/minimum.rs create mode 100644 codes/rust_lang/part06/src/something_or_nothing.rs diff --git a/codes/rust_lang/part02/src/main.rs b/codes/rust_lang/part02/src/main.rs index acf2592..52e0a8d 100644 --- a/codes/rust_lang/part02/src/main.rs +++ b/codes/rust_lang/part02/src/main.rs @@ -1,4 +1,4 @@ -/// In part03 we introduce `Enums` (also known as `Algebraic Data Types`), `Pattern Matching`, +/// In part02 we introduce `Enums` (also known as `Algebraic Data Types`), `Pattern Matching`, /// and `Static Functions`. enum NumberOrNothing { diff --git a/codes/rust_lang/part04/Cargo.toml b/codes/rust_lang/part04/Cargo.toml new file mode 100644 index 0000000..7324487 --- /dev/null +++ b/codes/rust_lang/part04/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "part04" +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/codes/rust_lang/part04/src/main.rs b/codes/rust_lang/part04/src/main.rs new file mode 100644 index 0000000..08d81e3 --- /dev/null +++ b/codes/rust_lang/part04/src/main.rs @@ -0,0 +1,117 @@ +/*! +Part04 illustrates the concepts of **Ownership** and **Borrowing**. It also +presents the manual implementation of [Clone] and [Copy]. +*/ + +// Rust basics: +// - Ownership +// - Borrowing +// - Clone +// - Copy + +// An generic enumerated type that has two variants. +// +// - Nothing +// - Something +enum SomethingOrNothing<T> { + // A [SomethingOrNothing::Nothing] + Nothing, + // A [SomethingOrNothing::Something] encapsulating a T + Something(T), +} + +impl<T: std::fmt::Display> SomethingOrNothing<T> { + // A static function that prints the content of a SomethingOrNothing. + fn print(&self) { + match self { + SomethingOrNothing::Nothing => println!("Nothing."), + SomethingOrNothing::Something(val) => println!("Something is: {}", val), + } + } +} + +// Manual implementation of [Clone] +impl<T: Clone> Clone for SomethingOrNothing<T> { + fn clone(&self) -> Self { + match self { + SomethingOrNothing::Nothing => SomethingOrNothing::Nothing, + SomethingOrNothing::Something(val) => SomethingOrNothing::Something(val.clone()), + } + } +} + +// Manual implementation of [Copy] +impl<T: Copy> Copy for SomethingOrNothing<T> {} + +// If we remove Copy, we have a problem with the t in tab +// in the computation of the minimum. +trait Minimum: Copy { + fn min(self, rhs: Self) -> Self; +} + +impl<T: Minimum> Minimum for SomethingOrNothing<T> { + fn min(self, rhs: Self) -> Self { + match (self, rhs) { + (SomethingOrNothing::Nothing, SomethingOrNothing::Nothing) => { + SomethingOrNothing::Nothing + } + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Something(rhs)) => { + SomethingOrNothing::Something(lhs.min(rhs)) + } + (SomethingOrNothing::Nothing, SomethingOrNothing::Something(rhs)) => { + SomethingOrNothing::Something(rhs) + } + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Nothing) => { + SomethingOrNothing::Something(lhs) + } + } + } +} + +impl Minimum for i32 { + fn min(self, rhs: Self) -> Self { + if self < rhs { + self + } else { + rhs + } + } +} + +const SIZE: usize = 9; + +// Poorly emulates the parsing of a command line. +fn read_command_line() -> [i32; SIZE] { + [10, 32, 12, 43, 52, 53, 83, 2, 9] +} + +// Prints all the elements of the `tab`. +// Tab is borrowed here +fn print_tab(tab: &[i32; SIZE]) { + for t in tab { + print!("{} ", t); + } + println!(); +} + +// Computes the minimum of a borrowed Array of a type T which implements the [Minimum] trait. +// Returns a [SomethingOrNothing::Something] containing the the minimum value +// or [SomethingOrNothing::Nothing] if no minimum value was found. +fn find_min<T: Minimum>(tab: &[T; SIZE]) -> SomethingOrNothing<T> { + let mut minimum = SomethingOrNothing::Nothing; + // Here is T is Copyable. Which means that t is not moved in the loop + for t in tab { + minimum = minimum.min(SomethingOrNothing::Something(*t)); + } + minimum +} + +fn main() { + let tab = read_command_line(); + println!("Among the Somethings in the list:"); + print_tab(&tab); + // There are alternatives to access fields of tuples + let min = find_min(&tab); + // The first field is not used therefore we can replace it with "_" + min.print(); +} diff --git a/codes/rust_lang/part05/Cargo.toml b/codes/rust_lang/part05/Cargo.toml new file mode 100644 index 0000000..bc59639 --- /dev/null +++ b/codes/rust_lang/part05/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "part05" +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/codes/rust_lang/part05/src/main.rs b/codes/rust_lang/part05/src/main.rs new file mode 100644 index 0000000..a135c6a --- /dev/null +++ b/codes/rust_lang/part05/src/main.rs @@ -0,0 +1,213 @@ +/*! +This is an example of Rust crate comments (or inner comments). +They will be rendered in the front page of your (crate) library. + +In this program we wrote an algorithm that computes the minimum of +a sequence of integers. + +To create the documentation run the command +```bash +cargo doc +``` +The obtain documentation can be found in the `target/doc/part04/index.html` directory + +To view the documentation type +```bash +cargo doc --open +``` +which will open the browser and show you the documentation. + +The documentation supports the CommonMarkdown syntax. + +Below we will use the `///` comments that will comment the code directly below. +We can also sue `//` but they will not be rendered. +Each line written here could be prefixed by `//!` instead of enclosed in `/*! */`. + +For more informations about writing documentation [follow that link](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html). + +Also Rust comes with great tooling. +- Clippy: A linter. +- Rustfmt: A formatter. +*/ + +// Rust basics: +// - Tests +// - Copy/Clone via derive +// - PartialEq +// - Documentation +// - clippy, rustfmt + +/// An generic enumerated type that has two variants that are [Clone] +/// and [Copy] using derive. +/// +/// - Nothing +/// - Something +#[derive(Clone, Copy)] +enum SomethingOrNothing<T> { + /// A [SomethingOrNothing::Nothing] + Nothing, + /// A [SomethingOrNothing::Something] encapsulating a T + Something(T), +} + +impl<T: std::fmt::Display> SomethingOrNothing<T> { + /// A static function that prints the content of a SomethingOrNothing. + fn print(&self) { + match self { + SomethingOrNothing::Nothing => println!("Nothing."), + SomethingOrNothing::Something(val) => println!("Something is: {}", val), + } + } +} + +impl<T> Default for SomethingOrNothing<T> { + /// By Default a [SomethingOrNothing] is a nothing. + fn default() -> Self { + SomethingOrNothing::Nothing + } +} + +impl<T: PartialEq> PartialEq for SomethingOrNothing<T> { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (SomethingOrNothing::Nothing, SomethingOrNothing::Nothing) => true, + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Something(rhs)) => { + *lhs == *rhs + } + _ => false, + } + } +} + +// If we remove Copy, we have a problem with the t in tab +// in the computation of the minimum. +trait Minimum: Copy { + fn min(self, rhs: Self) -> Self; +} + +impl<T: Minimum> Minimum for SomethingOrNothing<T> { + fn min(self, rhs: Self) -> Self { + match (self, rhs) { + (SomethingOrNothing::Nothing, SomethingOrNothing::Nothing) => { + SomethingOrNothing::Nothing + } + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Something(rhs)) => { + SomethingOrNothing::Something(lhs.min(rhs)) + } + (SomethingOrNothing::Nothing, SomethingOrNothing::Something(rhs)) => { + SomethingOrNothing::Something(rhs) + } + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Nothing) => { + SomethingOrNothing::Something(lhs) + } + } + } +} + +// i32 is Copyable as a very basic type as f32, f64, etc. +// Arrays for example are not copyable. +impl Minimum for i32 { + fn min(self, rhs: Self) -> Self { + if self < rhs { + self + } else { + rhs + } + } +} + +const SIZE: usize = 9; + +/// Poorly emulates the parsing of a command line. +fn read_command_line() -> [i32; SIZE] { + [10, 32, 12, 43, 52, 53, 83, 2, 9] +} + +/// Prints all the elements of the `tab` and returns `tab`. +fn print_tab(tab: &[i32; SIZE]) { + for t in tab { + print!("{} ", t); + } + println!(); +} + +/// Computes the minimum of an Array of a type T which implements the [Minimum] trait. +/// Returns the array and a [SomethingOrNothing::Something] containing the the minimum value +/// or [SomethingOrNothing::Nothing] if no minimum value was found. +/// +/// # Example +/// +/// ``` +/// # fn main() { +/// let tab = [10, 32, 12, 43, 52, 53, 83, 2, 9]; +/// let min = find_min(&tab); +/// assert!(min == SomethingOrNothing::Something(2)); +/// # } +/// ``` +fn find_min<T: Minimum>(tab: &[T; SIZE]) -> SomethingOrNothing<T> { + let mut min = SomethingOrNothing::Nothing; + // Here is T is not Copyable tab is consumed and cannot be returned + for t in tab { + min = min.min(SomethingOrNothing::Something(*t)); + } + min +} + +fn main() { + let tab = read_command_line(); + + println!("Among the Somethings in the list:"); + print_tab(&tab); + + // There are alternatives to access fields of tuples + let min = find_min(&tab); + // The first field is not used therefore we can replace it with "_" + min.print(); +} + +#[test] +fn test_creation() { + let n1: SomethingOrNothing<i32> = SomethingOrNothing::default(); + assert!(n1 == SomethingOrNothing::Nothing); + let n2: SomethingOrNothing<i32> = SomethingOrNothing::Something(1); + assert!(n2 == SomethingOrNothing::Something(1)); +} + +#[test] +#[should_panic] +fn test_failure_creation() { + let n2: SomethingOrNothing<i32> = SomethingOrNothing::Something(1); + assert!(n2 == SomethingOrNothing::Nothing); + assert!(n2 == SomethingOrNothing::Something(2)); +} + +#[test] +fn test_min() { + let a = [1, 5, -1, 2, 0, 10, 11, 0, 3]; + let min = find_min(&a); + assert!(min == SomethingOrNothing::Something(-1)); +} + +#[cfg(test)] +mod tests { + #[test] + fn test_min() { + let x = 5; + let y = 10; + assert_eq!(x.min(y), x); + assert_eq!(y.min(x), x); + } + + #[test] + fn test_min_something_or_nothing() { + use crate::Minimum; + let x = crate::SomethingOrNothing::Something(5i32); + let y = crate::SomethingOrNothing::Something(10i32); + let z = crate::SomethingOrNothing::Nothing; + assert!(x.min(y) == x); + assert!(y.min(x) == x); + assert!(z.min(y) == y); + assert!(y.min(z) == y); + assert!(z.min(z) == z); + } +} diff --git a/codes/rust_lang/part06/Cargo.toml b/codes/rust_lang/part06/Cargo.toml new file mode 100644 index 0000000..56d9710 --- /dev/null +++ b/codes/rust_lang/part06/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "part06" +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/codes/rust_lang/part06/src/io.rs b/codes/rust_lang/part06/src/io.rs new file mode 100644 index 0000000..8c14560 --- /dev/null +++ b/codes/rust_lang/part06/src/io.rs @@ -0,0 +1,13 @@ +/// Poorly emulates the parsing of a command line. +pub fn read_command_line() -> [i32; crate::SIZE] { + [10, 32, 12, 43, 52, 53, 83, 2, 9] +} + +/// Prints all the elements of the `tab`. +/// Tab is borrowed here +pub fn print_tab(tab: &[i32; crate::SIZE]) { + for t in tab { + print!("{} ", t); + } + println!(); +} diff --git a/codes/rust_lang/part06/src/lib.rs b/codes/rust_lang/part06/src/lib.rs new file mode 100644 index 0000000..e3b0224 --- /dev/null +++ b/codes/rust_lang/part06/src/lib.rs @@ -0,0 +1,61 @@ +/*! +Part06 illustrates the concepts of **Ownership** and **Borrowing**. It also +presents [Clone] and [Copy] traits through derive. +*/ + +const SIZE: usize = 9; + +pub mod io; +mod minimum; +pub mod something_or_nothing; + +#[cfg(test)] +mod tests { + use crate::minimum::Minimum; + use crate::something_or_nothing::{find_min, SomethingOrNothing}; + + #[test] + fn test_creation() { + let n1: SomethingOrNothing<i32> = SomethingOrNothing::default(); + assert!(n1 == SomethingOrNothing::Nothing); + let n2: SomethingOrNothing<i32> = SomethingOrNothing::Something(1); + assert!(n2 == SomethingOrNothing::Something(1)); + } + + #[test] + #[should_panic] + fn test_failure_creation() { + let n2: SomethingOrNothing<i32> = SomethingOrNothing::Something(1); + assert!(n2 == SomethingOrNothing::Nothing); + assert!(n2 == SomethingOrNothing::Something(2)); + } + + #[test] + fn test_min() { + let a = [1, 5, -1, 2, 0, 10, 11, 0, 3]; + let min = find_min(&a); + assert!(min == SomethingOrNothing::Something(-1)); + } + + #[test] + fn test_min_i32() { + let x = 5; + let y = 10; + assert_eq!(Minimum::min(x, y), x); + assert_eq!(Minimum::min(y, x), x); + assert_eq!(Minimum::min(x, x), x); + assert_eq!(Minimum::min(y, y), y); + } + + #[test] + fn test_min_something_or_nothing() { + let x = SomethingOrNothing::Something(5i32); + let y = SomethingOrNothing::Something(10i32); + let z = SomethingOrNothing::Nothing; + assert!(x.min(y) == x); + assert!(y.min(x) == x); + assert!(z.min(y) == y); + assert!(y.min(z) == y); + assert!(z.min(z) == z); + } +} diff --git a/codes/rust_lang/part06/src/main.rs b/codes/rust_lang/part06/src/main.rs new file mode 100644 index 0000000..5501e75 --- /dev/null +++ b/codes/rust_lang/part06/src/main.rs @@ -0,0 +1,12 @@ +use part06::io; +use part06::something_or_nothing::find_min; + +fn main() { + let tab = io::read_command_line(); + println!("Among the Somethings in the list:"); + io::print_tab(&tab); + // There are alternatives to access fields of tuples + let min = find_min(&tab); + // The first field is not used therefore we can replace it with "_" + min.print(); +} diff --git a/codes/rust_lang/part06/src/minimum.rs b/codes/rust_lang/part06/src/minimum.rs new file mode 100644 index 0000000..7d7a654 --- /dev/null +++ b/codes/rust_lang/part06/src/minimum.rs @@ -0,0 +1,15 @@ +// If we remove Copy, we have a problem with the t in tab +// in the computation of the minimum. +pub trait Minimum: Copy { + fn min(self, rhs: Self) -> Self; +} + +impl Minimum for i32 { + fn min(self, rhs: Self) -> Self { + if self < rhs { + self + } else { + rhs + } + } +} diff --git a/codes/rust_lang/part06/src/something_or_nothing.rs b/codes/rust_lang/part06/src/something_or_nothing.rs new file mode 100644 index 0000000..32b3007 --- /dev/null +++ b/codes/rust_lang/part06/src/something_or_nothing.rs @@ -0,0 +1,84 @@ +use crate::minimum::Minimum; + +/// An generic enumerated type that has two variants. +/// +/// - Nothing +/// - Something +#[derive(Clone, Copy)] +pub enum SomethingOrNothing<T> { + /// A [SomethingOrNothing::Nothing] + Nothing, + /// A [SomethingOrNothing::Something] encapsulating a T + Something(T), +} + +impl<T: std::fmt::Display> SomethingOrNothing<T> { + /// A static function that prints the content of a SomethingOrNothing. + pub fn print(&self) { + match self { + SomethingOrNothing::Nothing => println!("Nothing."), + SomethingOrNothing::Something(val) => println!("Something is: {}", val), + } + } +} + +impl<T> Default for SomethingOrNothing<T> { + /// By Default a [SomethingOrNothing] is a nothing. + fn default() -> Self { + SomethingOrNothing::Nothing + } +} + +impl<T: PartialEq> PartialEq for SomethingOrNothing<T> { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (SomethingOrNothing::Nothing, SomethingOrNothing::Nothing) => true, + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Something(rhs)) => { + *lhs == *rhs + } + _ => false, + } + } +} + +impl<T: Minimum> Minimum for SomethingOrNothing<T> { + fn min(self, rhs: Self) -> Self { + match (self, rhs) { + (SomethingOrNothing::Nothing, SomethingOrNothing::Nothing) => { + SomethingOrNothing::Nothing + } + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Something(rhs)) => { + SomethingOrNothing::Something(lhs.min(rhs)) + } + (SomethingOrNothing::Nothing, SomethingOrNothing::Something(rhs)) => { + SomethingOrNothing::Something(rhs) + } + (SomethingOrNothing::Something(lhs), SomethingOrNothing::Nothing) => { + SomethingOrNothing::Something(lhs) + } + } + } +} + +/// Computes the minimum of an Array of a type T which implements the [Minimum] trait. +/// Returns a [SomethingOrNothing::Something] containing the the minimum value +/// or [SomethingOrNothing::Nothing] if no minimum value was found. +/// +/// # Example +/// +/// ``` +/// # use part06::something_or_nothing::{SomethingOrNothing, find_min}; +/// # fn main() { +/// let tab = [10, 32, 12, 43, 52, 53, 83, 2, 9]; +/// let min = find_min(&tab); +/// assert!(min == SomethingOrNothing::Something(2)); +/// # } +/// ``` +pub fn find_min<T: Minimum>(tab: &[T; crate::SIZE]) -> SomethingOrNothing<T> { + let mut minimum = SomethingOrNothing::Nothing; + // Here is T is Copyable. Which means that t is not moved in the loop + for t in tab { + minimum = minimum.min(SomethingOrNothing::Something(*t)); + } + minimum +} -- GitLab