The num crate provides a unified set of numeric traits and types that extend Rust's standard library, allowing you to write generic code that works across integers, floats, and complex numbers. You primarily use the num_traits crate for traits like ToPrimitive and Zero, and the num crate itself for specific types like BigInt or Complex.
Add the dependencies to your Cargo.toml:
[dependencies]
num = "0.4"
num-traits = "0.2"
For generic arithmetic, import the relevant traits from num_traits and constrain your type parameters. This lets you write functions that accept any numeric type supporting the operation, rather than hardcoding i32 or f64.
use num_traits::{Zero, One, ToPrimitive};
fn calculate_sum<T>(a: T, b: T) -> T
where
T: Zero + One + std::ops::Add<Output = T>,
{
a + b
}
fn main() {
// Works with standard types
let int_sum = calculate_sum(10i32, 5i32);
// Works with floats
let float_sum = calculate_sum(10.5f64, 2.5f64);
// Convert to primitive if needed
if let Some(val) = int_sum.to_f64() {
println!("Converted to f64: {}", val);
}
}
When you need types beyond standard limits, such as arbitrary-precision integers or complex numbers, use the concrete types provided by the num crate. BigInt and BigUint handle numbers of any size, while Complex supports mathematical operations on complex numbers.
use num::BigInt;
use num::Complex;
fn main() {
// Arbitrary precision integer
let big_num = BigInt::from(1000000000000000000000000000000u128);
let result = big_num * big_num;
println!("Big integer result: {}", result);
// Complex number arithmetic
let c1 = Complex::new(1.0, 2.0);
let c2 = Complex::new(3.0, 4.0);
let c_sum = c1 + c2;
println!("Complex sum: {}", c_sum);
}
Remember that num_traits is a dependency of num, so importing num often pulls in the traits, but explicitly importing num_traits is clearer for generic code. This setup ensures your numeric logic remains flexible and type-safe without sacrificing performance for standard types.