commit e8b31ec56e86b32c61ef32c53980c85441142ec4 Author: dqnid Date: Tue Aug 13 08:54:05 2024 +0200 feat(arrived at types) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..fba4807 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust-by-example" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..caf6b3e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rust-by-example" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/src/customtypes.rs b/src/customtypes.rs new file mode 100644 index 0000000..abfe82f --- /dev/null +++ b/src/customtypes.rs @@ -0,0 +1,231 @@ +#![allow(dead_code)] + +pub fn custom_types_module() { + // struct + // 1. Tuple structs, basically named tuples + struct Pair(i32, i32); + let pair = Pair(1, 2); + let Pair(_int1, _int2) = pair; // tuple structs can be destructured + + // 2. C structs + struct Point { + x: f32, + y: f32, + } + + struct Polygon<'a, T> { + points: [Point; 1], + sliced_points: &'a [T], + } + + let point_top: Point = Point { x: 1.0, y: 1.0 }; + let point_list: [Point; 2] = [Point { x: 2.0, y: 3.0 }, Point { x: 3.0, y: 4.0 }]; + + let _point_borrow: Point = Point { + x: 6.0, + ..point_top + }; + + let _my_triangle = Polygon { + points: [point_top], + sliced_points: &point_list, + }; + + // 3. Unit structs, field-less, useful for generics + // 0 bytes of size, useful as markers and indicate variants + struct Unit; + let _unit = Unit; + + /* + impl Something for Unit { + ... + } + */ + + // EXERCISE 1: create a triangle struct and a rect_area function, use nested destruturing + #[derive(Debug, Clone)] // to allow printing, and cloning, last one with a performace loss + struct MyPoint { + x: f32, + y: f32, + } + + fn distance(a: MyPoint, b: MyPoint) -> f32 { + return ((b.x - a.x).powf(2.0) + (b.y - a.y).powf(2.0)).sqrt(); + } + + struct Rectangle { + corners: [MyPoint; 4], + } + + fn rect_area(rect: Rectangle) -> f32 { + let Rectangle { corners } = rect; + + for i in 0..corners.len() { + println!("Corner {}: {:?}", i, corners[i]) + } + + return distance(corners[0].clone(), corners[1].clone()) + * distance(corners[0].clone(), corners[2].clone()); + } + + let my_rectangle = Rectangle { + corners: [ + MyPoint { x: 0.0, y: 0.0 }, + MyPoint { x: 4.0, y: 0.0 }, + MyPoint { x: 0.0, y: 4.0 }, + MyPoint { x: 4.0, y: 4.0 }, + ], + }; + + let my_rectangle_area = rect_area(my_rectangle); + println!("My rectangle area: {}", my_rectangle_area); + + // EXERCISE 2: create a function that returns a square out of a point + fn square(p: MyPoint, size: f32) -> Rectangle { + return Rectangle { + corners: [ + MyPoint { x: p.x, y: p.y }, // don't know yet how to use borrowing properly + MyPoint { + x: p.x + size, + y: p.y, + }, + MyPoint { + x: p.x, + y: p.y + size, + }, + MyPoint { + x: p.x + size, + y: p.y + size, + }, + ], + }; + } + + let my_square = square(MyPoint { x: 0.0, y: 0.0 }, 2.0); + + println!("My square: "); + for i in 0..my_square.corners.len() { + match my_square.corners.get(i) { + Some(xval) => print!("{:?}", xval), + None => println!("You went too far"), + } + } + + // enum + // ill make literally the example on rust-by-example + enum WebEvent { + PageLoad, + PageUnload, + Click { x: i64, y: i64 }, + } + + fn inspect_web_event(event: WebEvent) { + match event { + // switch over the enum + WebEvent::PageLoad => println!("Page loaded"), + WebEvent::PageUnload => println!("Page unload"), + WebEvent::Click { x, y } => println!("clicked at {}, {}", x, y), + } + } + + let load = WebEvent::PageLoad; + let unload = WebEvent::PageUnload; + let click = WebEvent::Click { x: 20, y: 0 }; + + inspect_web_event(load); + inspect_web_event(unload); + inspect_web_event(click); + + // enums can be used over a type alias + type MyEventType = WebEvent; + + let another_click = MyEventType::Click { x: 0, y: 20 }; + inspect_web_event(another_click); + + // can also use "use crate::" to avoid manual scoping + use WebEvent::{Click, PageUnload}; + + let yet_another_click = Click { x: 10, y: 0 }; + let another_unload = PageUnload; + inspect_web_event(yet_another_click); + inspect_web_event(another_unload); + + // c-like enums + enum Number { + // implicit start at 0 + Zero, + One, + Two, + } + enum Color { + Red = 0xff0000, + Green = 0x00ff00, + Blue = 0x0000ff, + } + + println!("zero is {}", Number::Zero as i32); + println!("one is {}", Number::One as i32); + + println!("roses are #{:06x}", Color::Red as i32); + println!("violets are #{:06x}", Color::Blue as i32); + + // Linked list implemented with enums + + enum List { + Cons(u32, Box), // tuple struct with an element (u32) and a pointer to the next (Box) + Nil, + } + + impl List { + // attach methods to the enum + fn new() -> List { + Self::Nil + } + + fn prepend(self, elem: u32) -> List { + Self::Cons(elem, Box::new(self)) + } + + fn stringify(&self) -> String { + match *self { + Self::Cons(head, ref tail) => { + format!("{} {}", head, tail.stringify()) // recursive + } + Self::Nil => { + format!("Nil / End") + } + } + } + } + + let mut my_list = List::new(); + + my_list = my_list.prepend(1); + my_list = my_list.prepend(2); + my_list = my_list.prepend(3); + my_list = my_list.prepend(4); + my_list = my_list.prepend(5); + + println!("My linked list: {}", my_list.stringify()); + + #[derive(Debug)] + struct MyNewPoint { + x: i32, + y: T, + } + + let my_custom_point = MyNewPoint { x: 20, y: 10 }; + println!("A T type point {:?}", my_custom_point); + + // consts + const MYCONST: i32 = 10; + //static : possibly mutable variable with 'static lifetime + static MYSTATIC: &str = "Rust"; + + let a_number = 5; + println!( + "{} is {}", + a_number, + if a_number > MYCONST { "big" } else { "small" } + ); +} diff --git a/src/helloworld.rs b/src/helloworld.rs new file mode 100644 index 0000000..5e39440 --- /dev/null +++ b/src/helloworld.rs @@ -0,0 +1,128 @@ +pub fn hello_world_module() { + // Basic arguments + println!( + "{0} {1} {exclamation_argument} {exclamation_argument}", + "Hello", + "world", + exclamation_argument = "!" + ); + + // Numeric arguments formating + println!("Base 10 {}", 261); + println!("Base 2 (binary) {:b}", 261); + println!("Base 8 (octal) {:0}", 261); + println!("Base 16 (hexadecimal) {:x}", 261); + + // Justify text + println!("{text:>10}", text = "right"); + println!("{text:0>10}", text = "right"); + println!("{text:<10}", text = "left"); + println!("{text:0 { + name: &'a str, + age: u8, + } + + let peter = Person { + name: "Peter", + age: 20, + }; + println!("{:#?}", peter); + + // A better way of printing structured data is by customizing the outputm with fmt::Display + // this code should not be inside main nor a function, but will keep it here to maintain a sequential order + use std::fmt; // import the fmt module + + // NOTE: if you want to avoid writing fmt::... every time, it can also be destructured in the + // import: use std::fmt::{self, Formatter, Display}; + + struct Structure(i32); + + // implement the fmt::Display in order to use the {} marker + impl fmt::Display for Structure { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "My structure formated: {}", self.0) + } + } + + let my_structure = Structure(1); + println!("{}", my_structure); + + // Display list example + // we'll use the ? operator: + // write!(f, "{}", value)?; + // Explanation: try to run write!, if it errors return the error, otherwise continue + // This is the proper way of handling the posible error what return fmt::Result (returned by + // the write!() + struct List(Vec); + + impl fmt::Display for List { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Extract the value using tuple indexing, + // and create a reference to `vec`. + let vec = &self.0; + + write!(f, "[")?; + + // Iterate over `v` in `vec` while enumerating the iteration + // count in `count`. + for (count, v) in vec.iter().enumerate() { + // For every element except the first, add a comma. + // Use the ? operator to return on errors. + if count != 0 { + write!(f, ", ")?; + } + write!(f, "{}: {}", count, v)?; + } + + // Close the opened bracket and return a fmt::Result value. + write!(f, "]") + } + } + + let v = List(vec![1, 2, 3]); + println!("{}", v); + + // Formating can be specified via traits, there is one trait for each argument type (b,x,o,X, + // ...) + // The default argument {} is formated with the Display trait + + struct City { + name: &'static str, + lat: f32, + lon: f32, + } + + impl fmt::Display for City { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' }; + let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' }; + + write!( + f, + "{}: {:.3}°{} {:.3}°{}", + self.name, + self.lat.abs(), + lat_c, + self.lon.abs(), + lon_c + ) + } + } + + let salamanca = City { + name: "Salamanca", + lat: 53.34778, + lon: -6.259722, + }; + + println!("{}", salamanca) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..98dd0c0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,20 @@ +// normal comment +/* + * Multi-line comment + */ +// : /// Doc comment: generate library docs for the following item +// : //! Doc comment + +//mod helloworld; +//mod primitives; +//mod customtypes; +//mod variablebindings; +mod types; + +fn main() { + //helloworld::hello_world_module(); + //primitives::primitives_module(); + //customtypes::custom_types_module(); + //variablebindings::variable_bindings_module(); + types::types_module(); +} diff --git a/src/primitives.rs b/src/primitives.rs new file mode 100644 index 0000000..92b8c7d --- /dev/null +++ b/src/primitives.rs @@ -0,0 +1,124 @@ +#![allow(dead_code)] + +fn reverse_tuple(pair: (i32, bool)) -> (bool, i32) { + let (int_param, bool_param) = pair; + + (bool_param, int_param) +} + +pub fn primitives_module() { + // Scalar types + // signed integers: i8, i16, i132, 164, i128 + // signed integers: u8, u16, u132, u64, u128 + // floating point: f32, f64 + // char + // bool + // unit type (), only posible value is an empty tuple + //// + // Compound types + // Array [1,2,3] + // Tuples (1, true) + + let logical = true; // not necesary to specify the type if initialized + let a_float: f64 = 1.0; + let rare_integer = 5i32; // suffix annotation, = integer of 32 bits with a value of 5 + println!("{} {} {}", logical, a_float, rare_integer); + + // All are inmutable by default + // mut keyword + let mut mutable_int = 12i32; + println!("int: {}", mutable_int); + mutable_int = 21; + println!("int mutated: {}", mutable_int); + + let mutable_int = true; // variables can be overwritten with shadowing + println!("not an int anymore {}", mutable_int); + + let thousand = 1_000; + println!( + "1_000 is a good way to express {} in order to make it readable", + thousand + ); + + let cientific = 7.6e-4; + println!( + "Cientific notation is also valid, as 7.6e-4 would be stored as {}", + cientific + ); + + println!( + "Remember that the type can be specified with the number, as 1u32 is {}", + 1u32 + ); + + let my_tuple = (1, "corcho", true); + + println!( + "The second value of the tuple {:?} is {}", + my_tuple, my_tuple.1 + ); + + let another_tuple = (1i32, false); + println!( + "A function can also receive and return tuples, such as reverse return {:?} on {:?}", + reverse_tuple(another_tuple), + another_tuple + ); + + #[derive(Debug)] + struct EmulatedMatrix(f32, f32, f32, f32); + + let matrix = EmulatedMatrix(1.1, 1.4, 0.5, 7.3); + println!("Simulated: {:?}", matrix); + let EmulatedMatrix(a, b, c, d) = matrix; + println!("Data can also be destructured: {}, {}, {}, {}", a, b, c, d); + + // EXERCISE 1: fmt::Display Matrix (make it easy, you dont know loops yet) + println!("EXERCISE 1: Print a matrix with the tuple struct:"); + use std::fmt::{Display, Formatter, Result}; + + impl Display for EmulatedMatrix { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "({}, {})\n({} {})", &self.0, &self.1, &self.2, &self.3) + } + } + + println!("{}", matrix); + + // EXERCISE 2: transpose a EmulatedMatrix (make it easy, you dont know loops yet) + println!("EXERCISE 2: make a simple transpose function (later on we'll improve it with loops)"); + fn transpose(m: EmulatedMatrix) -> EmulatedMatrix { + let EmulatedMatrix(a, b, c, d) = m; + return EmulatedMatrix(a, c, b, d); + } + + println!("{}", transpose(matrix)); + + // Arrays + let mut onetofive: [i8; 5] = [1, 2, 3, 4, 5]; + println!("The fourth element is {}", onetofive[3]); + let allthrees: [i32; 10] = [3; 10]; + println!( + "All threes: {:?} have a size of {}", + allthrees, + allthrees.len() + ); + + use std::mem; + println!("All threes accupies {} bytes", mem::size_of_val(&allthrees)); + + // Sclice: pointer to a portion of an array + // '&' means borrowing + let onetothree = &mut onetofive[1..3]; + println!("One to three slice {:?}", onetothree); + onetothree[1] = 7; + println!("onetofive slice {:?} after mutating onetothree", onetofive); + + for i in 0..onetofive.len() + 1 { + // force a failure + match onetofive.get(i) { + Some(xval) => println!("{}: {}", i, xval), + None => println!("Slow down! {} is too far!", i), + } + } +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..fba61e6 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,20 @@ +pub fn types_module() { + // casting with as: types can be casted as long as the types overlap. + let decimal = 65.4321; + let integer: u8 = decimal as u8; + + println!("The decimal {} is casted to the u8 {}", decimal, integer); + + // dangerous carting can be done with some methods + unsafe { + println!( + "f32 -100.0 as u8 is : {}", + (-100.0_f32).to_int_unchecked::() + ); + } + + // type alias are also a thing + type Inch = u64; + let inch: Inch = 5; + println!("using type aliases {}", inch); +} diff --git a/src/variablebindings.rs b/src/variablebindings.rs new file mode 100644 index 0000000..59e0eb0 --- /dev/null +++ b/src/variablebindings.rs @@ -0,0 +1,27 @@ +pub fn variable_bindings_module() { + // mutability: variables are immutable by default + // we can say they are Frozen by default + let mut mutable_number = 1; + + mutable_number += 1; + println!("number mutated: {}", mutable_number); + + // scope: variables have block scope, and can be shadowed + let shadowed_variable = 1; + + { + let shadowed_variable = 2; + println!("shadowed inside block: {}", shadowed_variable); + } + println!("shadowed outside block: {}", shadowed_variable); + + // declare first approach: you can declare first and initialize later, but its not recommended. + let my_binding; + + my_binding = 2; + + println!( + "A variable can be initialized later even if the declaration has no type: {}", + my_binding + ); +}