Hi,
I'm creating a money manipulation library (just like the Dinero library in Javascript) that uses integers and makes the bankers rounding.
For now I don't know if I should use an enum
with currencies, a Struct or a String. Using an enum
is easy and I get autocomplete with all the possible options but I don't want to create a huge enum
with hundreds of currencies. Using a String will make it a little cumbersome to create new instances as it is needed to use String::from("USD")
and it is error prone.
Also, the currency will be compared between Money
instances as it is not possible to make calculations with money that have different currencies or different precision.
What should I use?
For now I'm using an enum
:
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Currency {
USD,
EUR,
JPY,
GBP,
AUD,
CNY
}
#[derive(Debug, Clone, Copy)]
pub struct Money {
/// The value of the currency in a specified precision
amount: i64,
/// The number of decimal cases
precision: u64,
/// The name of the currency
currency: Currency
}
impl Money {
pub fn new(amount: i64, precision: u64) -> Money {
Money {
amount,
precision,
currency: Currency::USD,
}
}
pub fn new_with_currency(amount: i64, precision: u64, currency: Currency) -> Money {
Money {
amount,
precision,
currency,
}
}
pub fn round(value: f64) -> i64 {
// don't trust the rounding system of the compiler.
// truncate all the decimals.
let mut num_int: i64 = value.trunc() as i64;
// calculate the decimals by the difference between
// the truncated integer and the given value
let num_dec = (value - (num_int as f64)).abs();
if num_dec == 0.5 {
// if the number is not even and is positive
if num_int % 2 != 0 && num_int > 0 {
// add one
num_int += 1;
}
// if the number is not even and is negative
else if num_int % 2 != 0 && num_int < 0 {
// subtract one: this is the symmetry between positive and
// negative numbers
num_int -= 1;
}
}
// round as normal:
// - greater than 0.5: adds one
// - lesser than 0.5: don't add
else if num_dec > 0.5 && num_int > 0 {
num_int += 1;
}
num_int
}
pub fn match_precision(money_1: &Money, money_2: &Money) -> bool {
money_1.get_precision() == money_2.get_precision()
}
pub fn match_currency(money_1: &Money, money_2: &Money) -> bool {
money_1.get_currency() == money_2.get_currency()
}
fn check(money_1: &Money, money_2: &Money) {
if Money::match_currency(money_1, money_2) == false {
panic!("Cannot add values of different currencies");
} else if Money::match_precision(money_1, money_2) == false {
panic!("Cannot add values with different precisions.");
}
}
}
impl std::ops::Add for Money {
type Output = Self;
fn add(self, value: Self) -> Self {
Money::check(&self, &value);
Money{
amount: self.amount + value.amount,
precision: self.precision,
currency: self.currency.clone(),
}
}
}
Edit: All the answer were very helpful. I chose to create a currency this way:
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub struct Currency {
name: &'static str,
symbol: &'static str
}
impl Currency {
/// Create a new currency with the specified name and symbol.
///
///
/// ## Example
///
///
/// ```
/// use denarii::denarius::{ Denarius, Currency };
/// let usd = Currency::new("USD", "$");
/// ```
pub fn new(name: &'static str, symbol: &'static str) -> Currency {
Currency {
name: name,
symbol: symbol
}
}
}
I didn't use [u8; 3]
because the struct wouldn't implement the Copy
, Eq
and PartialEq
traits.