rusty-money

Crates.iorusty-money
lib.rsrusty-money
version0.5.0
created_at2019-12-08 09:37:35.881647+00
updated_at2026-01-14 05:46:43.188912+00
descriptionParse, format and perform calculations with money safely.
homepage
repositoryhttps://github.com/varunsrin/rusty_money
max_upload_size
id187259
size260,285
Varun Srinivasan (varunsrin)

documentation

https://docs.rs/rusty-money

README

rusty-money

Build Status Latest Version Docs

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 Result types 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:

[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

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 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

Arithmetic

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());

Comparison

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

Rounding

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)

Allocation

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]

Formatting

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

Custom Currencies

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

Exchange Rates

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();

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 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());

Precision Differences

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)

Feature Flags

[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"] }

License

MIT

Commit count: 129

cargo fmt