A Rust crate for producing string representations of numbers, formatted according to international standards, e.g.
"1,000,000"
for US English"10,00,000"
for Indian English"1 000 000"
for French Frenchnum-format offers three principal APIs…
ToFormattedString
The ToFormattedString
trait is the simplist of the three APIs. Just call
to_formatted_string
on a type that implements it (all the integer types in the standard library
implement it) while providing a desired format (see picking a format below). That said, using
ToFormattedString
will always heap allocate; so it is the slowest of the three APIs and cannot
be used in a no_std
environment.
use num_format::{Locale, ToFormattedString};
fn main() {
let s = 1000000.to_formatted_string(&Locale::en);
assert_eq!(&s, "1,000,000");
}
Buffer
Using the Buffer
type is the fastest API, as it does not heap allocate. Instead, the
formatted representation is written into a stack-allocated buffer. As such, you can use it in a
no_std
environment.
Although this API is available for all the integer types in the standard library, it is not
available for types like num_bigint::BigInt
whose maximum size cannot be known in advance.
use num_format::{Buffer, Locale};
fn main() {
// Create a stack-allocated buffer...
let mut buf = Buffer::default();
// Write "1,000,000" into the buffer...
buf.write_formatted(&1000000, &Locale::en);
// Get a view into the buffer as a &str...
let s = buf.as_str();
// Do what you want with the &str...
assert_eq!("1,000,000", s);
}
WriteFormatted
The WriteFormatted
trait is in between the other two APIs. You can write a formatted
representation into any type that implements WriteFormatted
(all the types in the standard
library that implement io::Write
or fmt::Write
implement WriteFormatted
, such as
Vec
, String
, File
, etc.).
If you’re writing a number type that can use the Buffer
API, there is no heap allocation.
That said, the io::Write
and fmt::Write
machinery adds a bit of overhead; so it’s faster
to use the Buffer
type directly. This trait is not available in a no_std
environment.
use num_format::{Locale, WriteFormatted};
fn main() {
// Create a writer...
let mut writer = String::new(); // Could also be Vec::new(), File::open(...), ...
// Write "1,000,000" into the writer...
writer.write_formatted(&1000000, &Locale::en);
assert_eq!(&writer, "1,000,000");
}
Formatting options (e.g. which thousands separator to use, what the minus sign looks like, etc.)
are represented by the Format
trait. This crate offers three concrete implementations of
the Format
trait…
Locale
The Locale
type is a programatically generated enum representing formatting standards from the
Common Locale Data Repository, which is maintained by the Unicode Consortium and used by
Apple in macOS and iOS, by LibreOffice, by IBM in AIX, among others.
use num_format::{Grouping, Locale};
fn main() {
let locale = Locale::en;
assert_eq!(locale.grouping(), Grouping::Standard);
assert_eq!(locale.minus_sign(), "-");
assert_eq!(locale.name(), "en");
assert_eq!(locale.separator(), ",");
let locale2 = Locale::from_name("en").unwrap();
assert_eq!(locale, locale2);
let available = Locale::available_names();
println!("All of the locale names available in the Unicode database are...");
println!("{:#?}", available);
}
SystemLocale
(available behind feature flag with-system-locale
)The SystemLocale
type is another type that implements Format
. It allows you to access your
OS’s locale information. It has a very similar API to Locale
and should work on all major
operating systems (i.e. macOS, linux, the BSDs, and Windows).
Since this type requires several dependencies (especially on Windows), it is behind a feature
flag. To use it, include num-format = { version = "0.4.3", features = ["with-system-locale"] }
in your Cargo.toml
. Additionally, on Windows (but only on Windows), using SystemLocale
requires Clang 3.9 or higher.
use num_format::SystemLocale;
fn main() {
let locale = SystemLocale::default().unwrap();
println!("My system's default locale is...");
println!("{:#?}", &locale);
let available = SystemLocale::available_names().unwrap();
println!("My available locale names are...");
println!("{:#?}", available);
match SystemLocale::from_name("en_US") {
Ok(_) => println!("My system has the 'en_US' locale."),
Err(_) => println!("The 'en_US' locale is not included with my system."),
}
}
CustomFormat
CustomFormat
is the third and final type that implements Format
. You can use it to build
your own custom formats.
use num_format::{Buffer, Error, CustomFormat, Grouping};
fn main() -> Result<(), Error> {
let format = CustomFormat::builder()
.grouping(Grouping::Indian)
.minus_sign("🙌")
.separator("😀")
.build()?;
let mut buf = Buffer::new();
buf.write_formatted(&(-1000000), &format);
assert_eq!("🙌10😀00😀000", buf.as_str());
Ok(())
}
--no-default-features
with-system-locale
feature and you’re on Windows, Clang 3.9 or higher
is also required. See here for
installation instructions.Available features | What to put in your Cargo.toml |
---|---|
no_std | num-format = { version = "0.4.3", default-features = false } |
with-num-bigint | num-format = { version = "0.4.3", features = ["with-num-bigint"] } |
with-serde | num-format = { version = "0.4.3", features = ["with-serde"] } |
with-system-locale | num-format = { version = "0.4.3", features = ["with-system-locale"] } |
num-format is licensed under either of:
at your option.
Format
on your own type.&str
without heap allocation.Format
.CustomFormat
s.Format
.Buffer
).to_formatted_string
method.io::Write
or fmt::Write
, such as &mut [u8]
and &mut String
, a write_formatted
method for writing
formatted numbers.