rusty-money
Introduction
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:
- Safety by default. Operations return
Resulttypes if they can fail, and are labelled if they can cause precision loss. - Blazing fast performance. Each operation is designed to be as fast as possible without violating safety requirements.
- Ergonomic interfaces. Simple methods with runtime currency checks over generic constraints.
The library supports many features necessary for building high performance financial apps:
- Currencies – Supports 150+ ISO-4217 currencies and cryptocurrencies, lets you define new ones.
- Internationalization – Locale-aware formatting and parsing for displaying currencies internationally.
- Flexible Representations – Choose between 128-bit decimals or 64-bit ints for maximum precision or performance.
- Exchange – Built-in support for currency conversion and exchange rate management.
- Utilities – Helpers for common operations like splitting money safely and fairly.
- Serialization – Fast serialization and deserialization
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.
Quickstart
Add rusty-money to your Cargo.toml:
[]
= "0.5"
Create some money, do math, and split the bill:
use ;
// Create money from major or minor units
let total = from_major; // => $100.00
let tip = from_minor; // => $18.75
// Arithmetic returns Result for safety
let total = total.add.unwrap; // => $118.75
// Split fairly among 3 people (remainder goes to first shares)
let shares = total.split.unwrap;
// => [$39.59, $39.58, $39.58]
println!; // => $118.75
Money
Money is the core type for monetary calculations. It stores amounts as 128-bit decimals, providing precision up to 28 decimal places.
Creating Money
use ;
// From minor units (cents, pence, etc.)
from_minor; // => $10.00
// From major units (dollars, pounds, etc.)
from_major; // => $10.00
// From a decimal
use dec;
from_decimal; // => $10.50
// Parse from string (locale-aware)
from_str.unwrap; // => $1,000.99
from_str.unwrap; // => €1.000,99
from_str.unwrap; // => ₹1,00,00,000.99
Arithmetic
All arithmetic operations return Result to handle currency mismatches and overflow:
use ;
let a = from_major;
let b = from_major;
a.add.unwrap; // => $150.00
a.sub.unwrap; // => $50.00
a.mul.unwrap; // => $300.00
a.div.unwrap; // => $25.00
// Currency mismatch returns an error
let eur = from_major;
assert!;
Comparison
use ;
let hundred = from_major;
let fifty = from_major;
hundred.gt.unwrap; // => true
fifty.lt.unwrap; // => true
hundred.eq.unwrap; // => true
// Predicates
hundred.is_positive; // => true
hundred.is_negative; // => false
hundred.is_zero; // => false
Rounding
Money preserves maximum precision until you explicitly round:
use ;
let amount = from_str.unwrap;
amount.round; // => $10.01
amount.round; // => $10.00
amount.round; // => $10.00 (banker's rounding)
Allocation
Splitting money fairly is tricky—$100.00 split 3 ways can't be done evenly. rusty-money handles remainder distribution automatically:
use ;
let total = from_major;
// Equal split (remainder distributed to first shares)
let shares = total.split.unwrap;
// => [$33.34, $33.33, $33.33]
// Weighted allocation
let parts = total.allocate.unwrap;
// => [$70.00, $20.00, $10.00]
Formatting
Money formats according to its currency's locale:
use ;
let usd = from_major;
let eur = from_major;
let inr = from_major;
println!; // => -$2,000.00
println!; // => -€2.000,00
println!; // => -₹1,00,000.00
Custom Currencies
Define your own currencies using the define_currency_set! macro:
use ;
define_currency_set!;
let gold = from_major;
println!; // => 500G
Exchange Rates
Convert money between currencies using ExchangeRate and Exchange:
use ;
use dec;
// Create a rate: 1 USD = 0.85 EUR
let rate = new.unwrap;
// Convert directly
let usd = from_major;
let eur = rate.convert.unwrap; // => €85.00
// Or store rates in an Exchange for reuse
let mut exchange = new;
exchange.set_rate;
// Look up and convert
if let Some = exchange.get_rate
// Convenience method on Money
let euros = usd.exchange_to.unwrap;
Fast Money
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).
Usage
use ;
// Create from minor units (no conversion needed)
let fast = from_minor; // => $100.00
// Create from major units
let fast = from_major.unwrap;
// Fast arithmetic
let a = from_minor;
let b = from_minor;
let sum = a.add.unwrap; // => $15.00
// Convert to Money for advanced features
let money = sum.to_money;
let shares = money.split.unwrap;
// Convert back (strict mode errors on precision loss)
let fast_again = from_money.unwrap;
// Or use lossy conversion if you accept truncation
let fast_lossy = from_money_lossy;
Precision Differences
FastMoney truncates intermediate results to minor units, which can accumulate into different final values:
use ;
// With Money (high precision): $10.00 / 3 keeps full decimal precision
let money = from_major;
let divided = money.div.unwrap; // => $3.3333333...
let restored = divided.mul.unwrap; // => $10.00 (no loss)
// With FastMoney (low precision): truncates to minor units
let fast = from_major.unwrap;
let divided = fast.div.unwrap; // => $3.33 (truncated)
let restored = divided.mul.unwrap; // => $9.99 (1 cent lost)
Feature Flags
[]
# Default: ISO-4217 currencies only
= "0.5"
# Add cryptocurrency support
= { = "0.4", = ["crypto"] }
# Add FastMoney
= { = "0.4", = ["fast"] }
# Add serde serialization
= { = "0.4", = ["serde"] }
# Everything
= { = "0.4", = ["iso", "crypto", "fast", "serde"] }
License
MIT