Other number types: what Rust sends you to a crate for¶
Level: 101 → 201 · working knowledge
One line: Rust's standard library stops at fixed-width integers and binary floats. Complex numbers, fractions, integers with no ceiling and decimals all come from crates, where Python ships all four itself — and num, the crate most books name first, covers three of them, not four.
| You want | Python ships | Rust's std has | Rust crate | Page |
|---|---|---|---|---|
| A complex number | complex, with a literal: 2.1-1.2j |
nothing | num::complex::Complex ↗ |
Complex numbers |
| An exact fraction | fractions.Fraction |
nothing | num::rational::Ratio ↗ |
Rationals |
| An integer with no ceiling | int — every one of them |
i128 and u128, 39 digits |
num::BigInt ↗ |
Big integers |
| A decimal, for money | decimal.Decimal |
nothing — count whole cents in an integer | rust_decimal ↗ |
Decimals |
| A float with 50 digits | decimal, after getcontext().prec = 50 |
nothing | bigdecimal ↗, dashu-float ↗ |
Decimals — when 28 digits is not enough |
The nothing is measured: the item index of the rendered std docs for 1.98.0 (share/doc/rust/html/std/all.html) has no item named Complex, Ratio, Rational, BigInt or Decimal.
What num actually contains¶
num 0.4.3 writes almost no code of its own. Its src/lib.rs re-exports six smaller crates, and cargo add num locks these versions:
| Module | Crate | What you get |
|---|---|---|
num::bigint |
num-bigint 0.4.8 |
BigInt, BigUint |
num::complex |
num-complex 0.4.6 |
Complex, Complex32, Complex64 |
num::rational |
num-rational 0.4.2 |
Ratio, Rational32, Rational64, BigRational |
num::integer |
num-integer 0.1.47 |
the Integer trait — gcd, lcm, div_floor |
num::iter |
num-iter 0.1.46 |
range_step and friends |
num::traits |
num-traits 0.2.19 |
Zero, One, Num, Float — for code generic over number types |
There is no decimal type and no arbitrary-precision float in it. Those are separate crates with separate authors: rust_decimal, bigdecimal, dashu-float, and rug, which wraps the C libraries GMP and MPFR. You can also depend on one piece directly. cargo add num-rational still pulls in num-bigint, num-integer and num-traits (cargo tree lists them), and leaves out num-complex and num-iter.
Getting a crate into a program¶
Every crate program on these pages is src/main.rs in a Cargo project:
Updating crates.io index
Adding num v0.4.3 to dependencies
Features:
+ std
- alloc
- libm
- num-bigint
- rand
- serde
Updating crates.io index
Locking 8 packages to latest Rust 1.98.0 compatible versions
That writes num = "0.4.3" under [dependencies] — a range, not a pin, as Adding a dependency explains. What happens when you skip those commands and compile a loose file instead is A throwaway that needs a crate.
Which parts are machine-checked. The runner behind this library compiles loose .rs files with rustc and cannot link a crate. So each page has two kinds of program. The std-only one sits in examples/ and is checked like every other example here. The crate programs and the Python ones were run by hand — Cargo on rustc 1.98.0, Python 3.14.7 — and each trailing comment is checked against what the program printed.
The pages¶
| Lesson | Level | What it teaches |
|---|---|---|
| Complex numbers | 101 → 201 | Python has a complex literal and Rust has a two-field struct from num. a + b works only because the crate implements Add, and (-1) ** 0.5 changes type in Python where Rust hands back NaN |
| Rationals | 201 | Ratio and Fraction are one design. The differences are a ceiling that panics in debug and gives a wrong answer in release, a tie that rounds away from zero, and no literal, because new has to reduce |
| Big integers | 101 → 201 | u128 holds 34! and not 35!. BigInt grows like Python's int but divides like Rust: / truncates where Python's // floors, and floor division on a primitive is still unstable |
| Decimals | 201 | Whole cents first. Then rust_decimal next to Decimal: both keep 28 digits, but one overflows where the other rounds, and they build 0.1 from a float differently. Plus 50 digits of √2 three ways |
| Rust in Action §2.3.4, run | 101 → 201 | The book section that introduces num, checked claim by claim: the listing's printed output, what num covers, use, literals, new, and cargo-edit |
If you are coming from Python¶
Python has a numeric tower: int, Fraction, float and complex mix freely, and an expression is promoted to the widest type in it. Rust has no tower. Every operator is a trait implementation between two named types, so a crate supports exactly the pairs its author wrote:
from decimal import Decimal
from fractions import Fraction
print(1 + Fraction(1, 2)) # 3/2
print(Fraction(1, 2) + 0.5) # 1.0
print(1 + 2j + Fraction(1, 2)) # (1.5+2j)
Decimal('0.1') + 0.1 # TypeError: unsupported operand type(s) for +: 'decimal.Decimal' and 'float'
use num::rational::Ratio;
fn main() {
println!("{}", Ratio::new(1, 2) + 1); // 3/2
// Ratio::new(1, 2) + 0.5 // E0277: cannot add `{float}` to `Ratio<{integer}>`
}
Two things transfer. Decimal refuses a float in both languages, for the same reason: the float is already inexact. And Ratio + integer works, because num-rational implements it. What changes is that Python's quiet promotion from Fraction to float has no Rust version. The line that would lose exactness does not compile, so you convert on purpose or not at all. See Operators are traits for why every pairing is a separate impl.
The Python library's crosswalk ↗ has the same map as one table, starting from the Python name.
See also¶
- What a float actually stores — the one type std does give you, and why
0.1is not 0.1 - Making a float whole — the tie rules that come back in
Ratio::roundandDecimal::round - Writing a number down — the range each built-in width promises
- Comparing two numbers of different types — Rust in Action §2.3.3, the section before this one: the conversion Rust makes you write before
==, where Python compares exact values (Comparing anintwith afloat↗) - The Advanced exactness cluster — scaled integers, what
i128is exact about and when the denominators compound: when a crate is more than the problem needs - Machine numbers ↗ — the math library on what radix and precision decide, which is the difference between a binary float and a decimal one
Po polsku¶
Biblioteka standardowa Rusta (std) zna tylko liczby całkowite o stałej szerokości (i8…i128) i binarne liczby zmiennoprzecinkowe (f32, f64). Liczby zespolone (complex numbers), ułamki (rationals), liczby całkowite bez górnej granicy (big integers, arbitrary precision) i liczby dziesiętne (decimals) trzeba dociągnąć z crate'a. Python ma wszystkie cztery w standardzie: complex, fractions.Fraction, zwykły int i decimal.Decimal.
Pułapka z książek: crate num daje tylko trzy z nich — Complex, Ratio i BigInt. Liczb dziesiętnych i zmiennoprzecinkowych o dowolnej precyzji w nim nie ma; to osobne crate'y (rust_decimal, bigdecimal, dashu-float, rug). Druga różnica: Python sam awansuje typy w wyrażeniu (Fraction + float daje float), a Rust nie — każda para typów to osobna implementacja cechy (trait), więc linia, która zgubiłaby dokładność, po prostu się nie kompiluje.
Szukaj po polsku: liczby zespolone w Ruscie · ułamki rust num rational · rust bigint · rust_decimal pieniądze · typy liczbowe Python a Rust