Having used the rusty_money
crate for similar reasons, one of the missing features I would point out is an implementation for the multiplication operators, which is necessary for currency conversion . Or make it an explicit type for the purpose of rate conversions, like rusty_money::ExchangeRate
.
In terms of types, the sign boolean could be a type parameter :
pub struct Positive;
pub struct Negative;
pub struct Money<Sign> {
sign: Sign,
dollars: u64,
cents: u8,
// ...
}
pub type PMoney = Money<Positive>;
pub type NMoney = Money<Negative>;
impl PMoney {
pub fn new(dollars: u64, cents: u8) -> Self {
Self {
sign: Positive,
dollars,
cents,
// ...
}
}
}
impl NMoney {
pub fn new(dollars: u64, cents: u8) -> Self {
Self {
sign: Negative,
dollars,
cents,
// ...
}
}
}
One problem with this is now you have different types representing monetary values, depending on whether they are positive or negative. You'll have to implement the operators for each combination separately. 
And I guess the last thing is that rusty_money
offers higher precision than just two digits. It comes in handy for rounding fractional cents and other unusual cases that have to be dealt with when doing any kind of accounting. In fact, there are multiple rounding strategies to consider, and it might be important to offer more than just away-from-zero.
Sorry to compare your project to a more mature crate, but it is what came to mind.
Quick code review, while I'm looking: you don't need to make internal state mutable in some cases. This for instance: nmoney/src/money.rs at main · 0xMattB/nmoney (github.com)
Rather than binding the value mutably, mutating, and returning, you can return from either branch of an if
expression. And negation is an operator, so you do not need to multiply:
fn convert_money_to_whole(money: &Money) -> i64 {
let whole = ((money.dollars * 100) + money.cents as u64) as i64;
if money.positive {
whole
} else {
-whole
}
}
Similarly, this one: nmoney/src/money.rs at main · 0xMattB/nmoney (github.com) also does not need mutable state.
fn convert_whole_to_money(whole: i64) -> Money {
let (positive, whole) = if whole < 0 {
(false, -whole)
} else {
(true, whole)
};
Money {
dollars: (whole / 100) as u64,
cents: (whole % 100) as u8,
positive,
options: Options::new()
}
}
This shows a use of if
expression in a let
statement and pattern destructuring to "do two things at once" without mutating state .
Looks like you have a good number of tests! Love to see it.