Skip to content
Select theme

How to convert a number to a String

Short answer: n.to_string() returns a String for every integer and float type. format!("{n}") builds one with other text around it. Both go through Display, which is also where the one surprise lives: a whole float prints without its .0.

From a fresh project — cargo new num-demo, then replace src/main.rs with each block below. The project name doesn’t matter; nothing in the output depends on it.

i32: 42
u8: 255
i64: -9000
usize: 7
f64: 3.14
f32: 1

Every integer type — i8 through i128, u8 through u128, isize, usize — and both floats implement Display, and ToString is a blanket implementation over Display, so to_string() comes for free on all of them. The suffix on the literal is what picks the type here; a let annotation or a turbofish does the same job, the same choice as on the way in.

Look at the last line. 1.0f32 printed as 1.

format! returns the String instead of printing it, and takes the same spec after the colon. The specs that only make sense for numbers are the radix ones:

hex: 2a
HEX: 2A
octal: 52
binary: 101010
padded: 00101010
exp: 1.2345e3
42 is 2a in hex

{:x}, {:o} and {:b} come from the LowerHex, Octal and Binary traits, which only integer types implement — println!("{s:b}") on a &str fails with error[E0277]: the trait bound 'str: Binary' is not satisfied. {:X} is the uppercase spelling. {:e} is exponent form, and Rust normalises it to one digit before the point, so 1234.5 prints as 1.2345e3.

Width, fill, precision and {:?} are the same mini-language every other type uses, and How to format strings with println! and format! has the whole of it. Thousands separators are a spec too, and they get their own article later in this chapter.

Display and Debug print a float differently

Section titled “Display and Debug print a float differently”
display: 1 0.30000000000000004
debug: 1.0 0.30000000000000004
f32: 0.33333334
inf: inf

{} asks for Display and {:?} asks for Debug, and on a float they differ in one visible place: Debug keeps the .0, so a whole number never reads back as an integer. 1.0f64.to_string() is 1; format!("{one:?}") is 1.0. Text that has to round-trip — a config file, a log line another program parses — wants the {:?} spelling.

Display prints the shortest string that reads back as the same value. That is why 0.1 + 0.2 shows all seventeen digits, and why an f32 shows 0.33333334 where the same division in f64 gives 0.3333333333333333. f64::INFINITY prints as inf. How much precision each float actually carries is a separate question.

When the number is going into a String you already have

Section titled “When the number is going into a String you already have”

to_string() allocates a String and hands it over, so pushing it into a buffer you already have pays for two allocations to move one number. write!(buf, "{n}") appends the digits into the buffer itself. That case — the buffer, the way it grows, and the allocation counts — is How to append to a String without extra allocations.

The next article in this chapter reads an integer the user typed at the terminal, where trimming and error reporting are the whole job.