feat(arrived at types)

This commit is contained in:
2024-08-13 08:54:05 +02:00
commit e8b31ec56e
9 changed files with 564 additions and 0 deletions

231
src/customtypes.rs Normal file
View File

@@ -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<List>), // tuple struct with an element (u32) and a pointer to the next (Box<List>)
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<T> {
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" }
);
}

128
src/helloworld.rs Normal file
View File

@@ -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<width$}", text = "left", width = 10);
// With actual variables
let number: usize = 5;
let another: usize = 2;
println!("{number} {another}");
// Print structure types: fmt::Debug and "{:#?}"
#[derive(Debug)]
struct Person<'a> {
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<i32>);
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)
}

20
src/main.rs Normal file
View File

@@ -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();
}

124
src/primitives.rs Normal file
View File

@@ -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),
}
}
}

20
src/types.rs Normal file
View File

@@ -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::<u8>()
);
}
// type alias are also a thing
type Inch = u64;
let inch: Inch = 5;
println!("using type aliases {}", inch);
}

27
src/variablebindings.rs Normal file
View File

@@ -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
);
}