Rust examples: Difference between revisions
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