Rust examples: Difference between revisions

From bibbleWiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
=Introduction=
=Introduction=
The reason for this page is because MediaWiki gets a bit testy with over 1000 lines grrhhhhh so bits of the rust page are on here
The reason for this page is because MediaWiki gets a bit testy with over 1000 lines grrhhhhh so bits of the rust page are on here
=Implementing Your Types=
Another letsgetrusty. This adviced when make a library to play nice by implmenting
* Debug, Clone, Default, PartialEq
It suggested you consider also implementing
* JSON serialize ,deserialize
This code showed me how to optionalize it.
<syntaxhighlight lang="rs">
use std::{rc::Rc, sync::Arc};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Role {
    Admin,
    Standard,
    #[default]
    Guest,
}
#[derive(Debug, Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct DB {}
#[derive(Debug, Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct User {
    id: u32,
    name: String,
    role: Role,
    #[cfg_attr(feature = "serde", serde(skip))]
    db: Arc<DB>,
}
fn main() {
    let user = User {
        id: 123,
        name: "Bogdan".to_owned(),
        role: Role::Admin,
        db: Arc::new(DB {}),
    };
    println!("{:?}", user);
    let user2 = user.clone();
    println!("{:?}", user2);
    let guest = User::default();
    let guest2 = User::default();
    assert_eq!(guest, guest2);
    let user_str = "{ \"id\": 123, \"name\": \"Bogdan\", \"role\": \"Admin\" }";
    #[cfg(feature = "serde")]
    let user: User = serde_json::from_str(&user_str).unwrap();
    #[cfg(feature = "serde")]
    println!("{:?}", user);
}
fn is_normal<T: Sized + Send + Sync + Unpin>() {}
#[test]
fn normal_types() {
    is_normal::<User>();
}
</syntaxhighlight>
And here is the toml
<syntaxhighlight lang="toml">
[package]
name = "common-traits"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0", features = ["derive", "rc"], optional = true }
serde_json = "1.0"
[features]
serde = ["dep:serde"]
</syntaxhighlight>
=Date Handling in Rust
=Date Handling in Rust
Found the on twitter (not all bad) @orhanbalci Type conversion and dates are the worst in most languages so thought I would keep this.
Found the on twitter (not all bad) @orhanbalci Type conversion and dates are the worst in most languages so thought I would keep this.

Revision as of 01:52, 10 October 2024

Introduction

The reason for this page is because MediaWiki gets a bit testy with over 1000 lines grrhhhhh so bits of the rust page are on here

Implementing Your Types

Another letsgetrusty. This adviced when make a library to play nice by implmenting

  • Debug, Clone, Default, PartialEq

It suggested you consider also implementing

  • JSON serialize ,deserialize

This code showed me how to optionalize it.

use std::{rc::Rc, sync::Arc};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Role {
    Admin,
    Standard,
    #[default]
    Guest,
}

#[derive(Debug, Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct DB {}

#[derive(Debug, Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct User {
    id: u32,
    name: String,
    role: Role,
    #[cfg_attr(feature = "serde", serde(skip))]
    db: Arc<DB>,
}

fn main() {
    let user = User {
        id: 123,
        name: "Bogdan".to_owned(),
        role: Role::Admin,
        db: Arc::new(DB {}),
    };

    println!("{:?}", user);

    let user2 = user.clone();

    println!("{:?}", user2);

    let guest = User::default();

    let guest2 = User::default();

    assert_eq!(guest, guest2);

    let user_str = "{ \"id\": 123, \"name\": \"Bogdan\", \"role\": \"Admin\" }";

    #[cfg(feature = "serde")]
    let user: User = serde_json::from_str(&user_str).unwrap();

    #[cfg(feature = "serde")]
    println!("{:?}", user);
}

fn is_normal<T: Sized + Send + Sync + Unpin>() {}

#[test]
fn normal_types() {
    is_normal::<User>();
}

And here is the toml

[package]
name = "common-traits"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde = { version = "1.0", features = ["derive", "rc"], optional = true }
serde_json = "1.0"

[features]
serde = ["dep:serde"]

=Date Handling in Rust Found the on twitter (not all bad) @orhanbalci Type conversion and dates are the worst in most languages so thought I would keep this.

fn main() {
    // you can use chrono crate for date time operations.
    // chrono crate is more capable than standard library std::time module
    use chrono::prelude::*;
    use chrono::Duration;
    
    // you can retrieve current date in Utc timezone as follows
    let current_date = Utc::today();
    println!("Utc current date: {}", current_date);
    
    // you can retrieve current date in local timezone as follows
    let local_current_date = Local::today();
    println!("Local current date: {}", local_current_date);
    
    // you can retrieve current time in UTC as follows
    let current_time_utc = Utc::now();
    println!( "Utc current time: {}", current_time_utc);
    
    // you can retrieve current time in local time zone as follows
    let current_time_local = Local::now();
    println!("Local current time: {}", current_time_local);
    
    // you can add some duration to a chrono::Date
    // succ method gets succeeding date
    
    let today = Utc::today();
    
    let tomorrow = today + Duration::days(1);
    assert_eq!(today.succ(), tomorrow);
    
    // you can subtract some duration from a chrono
    // pred method gets previous date
    
    let today = Utc::today();
    
    let yesterday = today - Duration::days(1);
    assert_eq!(today.pred(), yesterday);
    
    // you can get UNIX timestamp (epoch) value for a datetime using
    
    // timestamp method of chrono::offset::TimeZone trait.
    
    // since timestamps have numeric representation, they are easy to store in db
    // and send through network. You can aldo prefer this notation in your APIs
    let dt = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 0);
    assert_eq!(dt.timestamp(), 1);
    
    //you can also get timestamp value of a datetime in milliseconds
    let dt = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 500);
    assert_eq!(dt.timestamp_millis(), 1500);
    
    //you can convert create a chrono::DateTime from a timestamp seconds
    let timestamp = 15;
    
    let datetime = Utc.timestamp(timestamp, 0);
    assert_eq!(datetime.timestamp(), 15);
    
    //you can get difference of two date times as follows
    let first = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 0);
    let second = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 2, 0);
    
    let difference: Duration = second.signed_duration_since(first);
    assert_eq!(difference, Duration::seconds(1));
    
    // you can also add and subtract duration from a DateTime struct
    
    let now = Utc::now();
    
    let yesterday_at_the_same_time = now - Duration::days(1);
    println!("Yesterday at the same time: {}", yesterday_at_the_same_time);
    
    //you can compare durations
    assert_eq! (Duration::days(1), Duration::hours(24));
    
    //you can format your DateTime struct using strftime formatting options
    let now = Utc::now();
    println!("{}", now. format( "%d.%m.%Y %H:%M"));
    
    //you can parse DateTime from a formatted string as below
    let some_time = NaiveDateTime::parse_from_str("01.10.2021 14:21", "%d.%m.%Y %H:%M").unwrap( );
    assert_eq!(some_time, NaiveDate::from_ymd(2021,10,1).and_hms(14,21,0));
}

Type State Pattern

In some cases we only want to be able to do some functions on a struct based on the state of the struct. In rust we could create a field in the struct called state and maintain which functions are allowed based on the current state. However rust provides a better approach referred to as the type state pattern. For example if we wanted make a password manager structure we could do this.

#![allow(unused)]

use std::collections::HashMap;
use std::marker::PhantomData;

struct Locked;
struct Unlocked;

// PasswordManager<Locked> != PasswordManager<Unlocked>

struct PasswordManager<State = Locked> {
    master_pass: String,
    passwords: HashMap<String, String>,
    state: PhantomData<State>,
}

// Locked Functions
impl PasswordManager<Locked> {
    pub fn unlock(self, master_pass: String) -> PasswordManager<Unlocked> {
        PasswordManager {
            master_pass: self.master_pass,
            passwords: self.passwords,
            state: PhantomData,
        }
    }
}

// Unlocked Functions
impl PasswordManager<Unlocked> {
    pub fn lock(self) -> PasswordManager<Locked> {
        PasswordManager {
            master_pass: self.master_pass,
            passwords: self.passwords,
            state: PhantomData,
        }
    }

    pub fn list_passwords(&self) -> &HashMap<String, String> {
        &self.passwords
    }

    pub fn add_password(&mut self, username: String, password: String) {
        self.passwords.insert(username, password);
    }
}

// Generic Functions
impl<State> PasswordManager<State> {
    pub fn encryption(&self) -> String {
        todo!()
    }

    pub fn version(&self) -> String {
        todo!()
    }
}

// Constructor
impl PasswordManager {
    pub fn new(master_pass: String) -> Self {
        PasswordManager {
            master_pass,
            passwords: Default::default(),
            state: PhantomData,
        }
    }
}

fn main() {
    let mut manager = PasswordManager::new("password123".to_owned());
    let manager = manager.unlock("password123".to_owned());
    manager.list_passwords();
    manager.lock();
}

The benefits to doing this are

  • Only functions allowed are displayed in the IDE. E.g. a locked password manager cannot see the functions for an unlocked password manager
  • The state PhantomData<State> is compiled out and reduces memory footprint