Rust examples

From bibbleWiki
Revision as of 01:49, 10 October 2024 by Iwiseman (talk | contribs)
Jump to navigation Jump to search

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 =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