Hi, i am fairly new to Rust, and thought this would be a more appropriate place to ask various smaller questions in one post (compared to e.g. StackOverflow).
So I have written these 2 files:
// utils.rs
pub struct Date {
pub num: u32,
}
pub enum TenorUnit {
D,
W,
M,
Y,
}
pub struct Tenor {
pub count: i16,
pub unit: TenorUnit,
}
impl Tenor {
pub fn new(count: i16, unit: TenorUnit) -> Tenor {
Tenor {
count: count,
unit: unit,
}
}
pub fn from_str(s: &str) -> Tenor {
let count = s[0..s.len() - 1].parse::<i16>().unwrap();
let unit = match s.chars().last().unwrap() {
'D' => TenorUnit::D,
'W' => TenorUnit::W,
'M' => TenorUnit::M,
'Y' => TenorUnit::Y,
_ => panic!("Invalid tenor unit ({})", s.chars().last().unwrap()),
};
Tenor {
count: count,
unit: unit,
}
}
}
pub trait TenorLike {
fn to_tenor(self) -> Tenor;
}
impl TenorLike for Tenor {
fn to_tenor(self) -> Tenor {
self
}
}
impl TenorLike for &str {
fn to_tenor(self) -> Tenor {
Tenor::from_str(self)
}
}
impl Date {
pub fn new(num: u32) -> Date {
Date { num }
}
pub fn add<T: TenorLike>(&self, t: T) -> Self {
let tenor = t.to_tenor();
let new_num = match tenor.unit {
TenorUnit::D => (self.num as i32 + tenor.count as i32) as u32,
TenorUnit::W => (self.num as i32 + tenor.count as i32 * 7) as u32,
TenorUnit::M => (self.num as i32 + tenor.count as i32 * 30) as u32,
TenorUnit::Y => (self.num as i32 + tenor.count as i32 * 365) as u32,
};
Date { num: new_num }
}
}
and
// main.rs
mod utils;
use crate::utils::Date;
use crate::utils::Tenor;
use crate::utils::TenorUnit;
fn main() {
let d = Date::new(44_528); // Excel format, 44528 equals 2021-11-28
let m1 = d.add(Tenor::new(3, TenorUnit::W)); // add 3 weeks
let m2 = d.add(Tenor::new(2, TenorUnit::M)); // add 2 months
let m3 = d.add(Tenor::new(1, TenorUnit::Y)); // add 1 year
let m4 = d.add(Tenor::from_str("3M")); // add 3 months
for m in [m1, m2, m3, m4].iter() {
println!("{}", m.num);
}
}
My questions are:
-
Are there ways to automatically convert enum values to strings, so that I don't have to manually write
'D' => TenorUnit::D
, etc everywhere? I know e.g. C++ cannot do this without macros or external libraries. Does Rust offer something here within its standard? -
When returning the final
Date
in theDate::add
function, what is "better" to write?Date{num:new_num}
orDate::new(new_num)
? Basically, I wonder ifDate { }
is faster as it creates the value "inline", whereasDate::new
is slower as it requires a superfluous function call tonew
(which then just callsDate { }
anyway)? -
In the
Date::add
function, there is a lot of casting in lines like this:
(self.num as i32 + tenor.count as i32 * 7) as u32
Is this the correct way to write this? Is it possible to reduce the number of times I need to cast? -
By using
trait TenorLike
I have tried to overload functionDate::add
, so that I can useTenor
inputs, as well asstr
inputs for convenience. Is there a more natural way to implement overloading (specifically, if the only second overload is a string)? Basically wondering if there is a way to remove boilerplate liketrait TenorLike { fn to_tenor(self) -> Tenor; }
? -
Is the way I implemented function
Tenor::from_str
ok, or should one always implement the trait:
impl FromStr for Tenor { fn from_str(s: &str) -> Result<...> { ... } }
?
The reason I am hesitant to use the trait is that I'd have to add.unwrap()
every time I useTenor::from_str
and adduse std::str::FromStr;
in my main.rs file, which adds an extra import & hence more boilerplate. And I feelfrom_str
should be included inuse crate::utils::Tenor;
to begin with.
Apologies if my questions are naive. I am just trying to get the basics right. Any other comments on my code are also appreciated.