| Crates.io | rusty-money |
| lib.rs | rusty-money |
| version | 0.5.0 |
| created_at | 2019-12-08 09:37:35.881647+00 |
| updated_at | 2026-01-14 05:46:43.188912+00 |
| description | Parse, format and perform calculations with money safely. |
| homepage | |
| repository | https://github.com/varunsrin/rusty_money |
| max_upload_size | |
| id | 187259 |
| size | 260,285 |
rusty-money is a library for handling monetary values in Rust.
It handles the complex parts of dealing with money: rounding, precision, parsing, i18n, exchange rates, and serialization. It follows ISO-4217 currency definitions and is inspired by Fowler's money pattern, Golang's go-money and Ruby's money.
The design principles behind rusty-money are:
Result types if they can fail, and are labelled if they can cause precision loss.The library supports many features necessary for building high performance financial apps:
There are three main interfaces:
Money — Uses 128-bit decimals for high precision and supports allocating, formatting and exchanging. The default option.
FastMoney — Uses 64-bit integers and truncates to minor units. Up to 5x faster for arithmetic but less precise and feature-rich.
Exchange — Manages currency exchange rates and converts between different currences.
Add rusty-money to your Cargo.toml:
[dependencies]
rusty-money = "0.5"
Create some money, do math, and split the bill:
use rusty_money::{Money, iso};
// Create money from major or minor units
let total = Money::from_major(100, iso::USD); // => $100.00
let tip = Money::from_minor(1875, iso::USD); // => $18.75
// Arithmetic returns Result for safety
let total = total.add(tip).unwrap(); // => $118.75
// Split fairly among 3 people (remainder goes to first shares)
let shares = total.split(3).unwrap();
// => [$39.59, $39.58, $39.58]
println!("{}", total); // => $118.75
Money is the core type for monetary calculations. It stores amounts as 128-bit decimals, providing precision up to 28 decimal places.
use rusty_money::{Money, iso};
// From minor units (cents, pence, etc.)
Money::from_minor(1000, iso::USD); // => $10.00
// From major units (dollars, pounds, etc.)
Money::from_major(10, iso::USD); // => $10.00
// From a decimal
use rust_decimal_macros::dec;
Money::from_decimal(dec!(10.50), iso::USD); // => $10.50
// Parse from string (locale-aware)
Money::from_str("1,000.99", iso::USD).unwrap(); // => $1,000.99
Money::from_str("1.000,99", iso::EUR).unwrap(); // => €1.000,99
Money::from_str("1,00,00,000.99", iso::INR).unwrap(); // => ₹1,00,00,000.99
All arithmetic operations return Result to handle currency mismatches and overflow:
use rusty_money::{Money, iso};
let a = Money::from_major(100, iso::USD);
let b = Money::from_major(50, iso::USD);
a.add(b).unwrap(); // => $150.00
a.sub(b).unwrap(); // => $50.00
a.mul(3).unwrap(); // => $300.00
a.div(4).unwrap(); // => $25.00
// Currency mismatch returns an error
let eur = Money::from_major(50, iso::EUR);
assert!(a.add(eur).is_err());
use rusty_money::{Money, iso};
let hundred = Money::from_major(100, iso::USD);
let fifty = Money::from_major(50, iso::USD);
hundred.gt(&fifty).unwrap(); // => true
fifty.lt(&hundred).unwrap(); // => true
hundred.eq(&hundred).unwrap(); // => true
// Predicates
hundred.is_positive(); // => true
hundred.is_negative(); // => false
hundred.is_zero(); // => false
Money preserves maximum precision until you explicitly round:
use rusty_money::{Money, Round, iso};
let amount = Money::from_str("10.005", iso::USD).unwrap();
amount.round(2, Round::HalfUp); // => $10.01
amount.round(2, Round::HalfDown); // => $10.00
amount.round(2, Round::HalfEven); // => $10.00 (banker's rounding)
Splitting money fairly is tricky—$100.00 split 3 ways can't be done evenly. rusty-money handles remainder distribution automatically:
use rusty_money::{Money, iso};
let total = Money::from_major(100, iso::USD);
// Equal split (remainder distributed to first shares)
let shares = total.split(3).unwrap();
// => [$33.34, $33.33, $33.33]
// Weighted allocation
let parts = total.allocate(vec![70, 20, 10]).unwrap();
// => [$70.00, $20.00, $10.00]
Money formats according to its currency's locale:
use rusty_money::{Money, iso};
let usd = Money::from_major(-2000, iso::USD);
let eur = Money::from_major(-2000, iso::EUR);
let inr = Money::from_major(-100000, iso::INR);
println!("{}", usd); // => -$2,000.00
println!("{}", eur); // => -€2.000,00
println!("{}", inr); // => -₹1,00,000.00
Define your own currencies using the define_currency_set! macro:
use rusty_money::{Money, define_currency_set};
define_currency_set!(
game {
GIL: {
code: "GIL",
exponent: 0,
locale: Locale::EnUs,
minor_units: 1,
name: "Gil",
symbol: "G",
symbol_first: false,
}
}
);
let gold = Money::from_major(500, game::GIL);
println!("{}", gold); // => 500G
Convert money between currencies using ExchangeRate and Exchange:
use rusty_money::{Money, Exchange, ExchangeRate, iso};
use rust_decimal_macros::dec;
// Create a rate: 1 USD = 0.85 EUR
let rate = ExchangeRate::new(iso::USD, iso::EUR, dec!(0.85)).unwrap();
// Convert directly
let usd = Money::from_major(100, iso::USD);
let eur = rate.convert(&usd).unwrap(); // => €85.00
// Or store rates in an Exchange for reuse
let mut exchange = Exchange::new();
exchange.set_rate(&rate);
// Look up and convert
if let Some(r) = exchange.get_rate(iso::USD, iso::EUR) {
let result = r.convert(&usd).unwrap();
println!("{}", result); // => €85.00
}
// Convenience method on Money
let euros = usd.exchange_to(iso::EUR, &exchange).unwrap();
FastMoney uses i64 minor units (cents) instead of 128-bit decimals, providing significantly faster arithmetic for performance-critical code paths. It comes with a narrower feature set and has lower precision due to the use of integers.
Only choose FastMoney over Money:
You're doing arithmetic operations with high-frequency and performance is critical.
Amounts fit within currency precision (no fractional cents).
use rusty_money::{FastMoney, Money, iso};
// Create from minor units (no conversion needed)
let fast = FastMoney::from_minor(10000, iso::USD); // => $100.00
// Create from major units
let fast = FastMoney::from_major(100, iso::USD).unwrap();
// Fast arithmetic
let a = FastMoney::from_minor(1000, iso::USD);
let b = FastMoney::from_minor(500, iso::USD);
let sum = a.add(b).unwrap(); // => $15.00
// Convert to Money for advanced features
let money = sum.to_money();
let shares = money.split(3).unwrap();
// Convert back (strict mode errors on precision loss)
let fast_again = FastMoney::from_money(money).unwrap();
// Or use lossy conversion if you accept truncation
let fast_lossy = FastMoney::from_money_lossy(fast_again.to_money());
FastMoney truncates intermediate results to minor units, which can accumulate into different final values:
use rusty_money::{FastMoney, Money, iso};
// With Money (high precision): $10.00 / 3 keeps full decimal precision
let money = Money::from_major(10, iso::USD);
let divided = money.div(3).unwrap(); // => $3.3333333...
let restored = divided.mul(3).unwrap(); // => $10.00 (no loss)
// With FastMoney (low precision): truncates to minor units
let fast = FastMoney::from_major(10, iso::USD).unwrap();
let divided = fast.div(3).unwrap(); // => $3.33 (truncated)
let restored = divided.mul(3).unwrap(); // => $9.99 (1 cent lost)
[dependencies]
# Default: ISO-4217 currencies only
rusty-money = "0.5"
# Add cryptocurrency support
rusty-money = { version = "0.4", features = ["crypto"] }
# Add FastMoney
rusty-money = { version = "0.4", features = ["fast"] }
# Add serde serialization
rusty-money = { version = "0.4", features = ["serde"] }
# Everything
rusty-money = { version = "0.4", features = ["iso", "crypto", "fast", "serde"] }
MIT