Rust int to string
last modified October 25, 2026
In this article we show how to convert integers to strings in Rust.
Integer to string conversion is a type conversion, where a value of an integer
type such as i32 or u64 is turned into its textual
representation. Rust does not convert numbers to strings implicitly; we cannot
add an integer to a string with +. The conversion is done
explicitly with the to_string method or with the formatting
macros.
The examples were tested with Rust 1.97 and the 2024 edition. They are run as
Cargo projects with cargo run -q.
Using to_string
The to_string method converts the integer value to a
String. It is available for every type that implements the
Display trait, which includes all integer types.
fn main() {
let val = 4;
let s = val.to_string();
let msg = String::from("There are ") + &s + " hawks";
println!("{msg}");
let big = u64::MAX.to_string();
let neg = i8::MIN.to_string();
println!("{big} has {} characters", big.len());
println!("{neg} has {} characters", neg.len());
}
In the program, we build a message from two strings and an integer.
let s = val.to_string();
let msg = String::from("There are ") + &s + " hawks";
The integer is converted to a String with
to_string. The + operator takes an owned
String on the left and a &str on the right, so
we borrow s with &.
let big = u64::MAX.to_string(); let neg = i8::MIN.to_string();
The method works for all integer types, including the largest unsigned and the smallest signed values. The minus sign is part of the resulting string.
$ cargo run -q There are 4 hawks 18446744073709551615 has 20 characters -128 has 4 characters
Using format!
The format! macro creates a String from a format
string and arguments. It is the most flexible way to put numbers into
text.
fn main() {
let val = 4;
let msg = format!("There are {val} hawks");
println!("{msg}");
let s = format!("{}", val * 10);
println!("{s}");
}
We build two strings with the format! macro.
let msg = format!("There are {val} hawks");
A variable name can be placed directly in the braces. The integer is converted and inserted into the text.
let s = format!("{}", val * 10);
Expressions are passed as arguments after the format string and inserted at
the empty {} placeholders. The format!("{}", x)
call gives the same result as x.to_string().
$ cargo run -q There are 4 hawks 40
Width, alignment, and sign
Format specifiers follow a colon inside the braces. They control the width of the output, the alignment, padding, and the sign.
fn main() {
let val = 42;
println!("[{val:5}]");
println!("[{val:<5}]");
println!("[{val:^5}]");
println!("[{val:05}]");
println!("[{val:+}]");
println!("[{:+}]", -val);
let width = 8;
println!("[{val:>width$}]");
}
The square brackets in the output show the width of each formatted value.
println!("[{val:5}]");
println!("[{val:<5}]");
println!("[{val:^5}]");
The number 5 is the minimum width. Numbers are right-aligned by default; the
< and ^ flags align them to the left and to the
center.
println!("[{val:05}]");
A leading zero pads the number with zeros instead of spaces.
println!("[{val:+}]");
The + flag prints the sign for positive numbers too.
let width = 8;
println!("[{val:>width$}]");
The width can be taken from a variable with the name$
syntax.
$ cargo run -q [ 42] [42 ] [ 42 ] [00042] [+42] [-42] [ 42]
Other number bases
Integers can be converted to binary, octal, and hexadecimal strings with the
b, o, x, and X format
types.
fn main() {
let val = 255;
println!("{val:b}");
println!("{val:o}");
println!("{val:x}");
println!("{val:X}");
println!("{val:#x}");
println!("{val:#010b}");
let neg: i8 = -1;
println!("{neg:b}");
}
The program prints 255 in several bases.
println!("{val:#x}");
println!("{val:#010b}");
The # flag adds the 0x, 0o, or
0b prefix. In the second line, the result is padded with zeros to
a width of 10 characters, including the prefix.
let neg: i8 = -1;
println!("{neg:b}");
Negative numbers are printed in two's complement form, using the bit width of their type.
$ cargo run -q 11111111 377 ff FF 0xff 0b11111111 11111111
Writing into an existing string
The format! macro always creates a new String. To
append formatted numbers to an existing string, we use the
write! macro.
use std::fmt::Write;
fn main() {
let mut s = String::new();
for i in 1..=5 {
write!(s, "{i} ").unwrap();
}
s.push_str(&100.to_string());
println!("{s}");
}
We build a string from numbers in a loop.
use std::fmt::Write;
The write! macro on a String requires the
std::fmt::Write trait to be in scope.
write!(s, "{i} ").unwrap();
The macro formats the number and appends it to s without
creating a temporary string. It returns a Result; writing into a
String never fails, so we call unwrap.
s.push_str(&100.to_string());
Alternatively, we convert the number with to_string and append
the result with push_str.
$ cargo run -q 1 2 3 4 5 100
Thousands separators
The standard library has no format specifier for grouping digits. We can insert the separators ourselves.
fn with_separators(n: u64) -> String {
let digits = n.to_string();
let mut res = String::new();
for (i, c) in digits.chars().enumerate() {
if i > 0 && (digits.len() - i).is_multiple_of(3) {
res.push(',');
}
res.push(c);
}
res
}
fn main() {
for n in [7, 1234, 1000000, 9876543210] {
println!("{}", with_separators(n));
}
}
The with_separators function converts the number to a string and
builds a new string with commas between groups of three digits.
if i > 0 && (digits.len() - i).is_multiple_of(3) {
res.push(',');
}
A comma is inserted before a digit if the number of digits that remain, including this one, is a multiple of three. No comma is placed before the first digit.
$ cargo run -q 7 1,234 1,000,000 9,876,543,210
Converting back with parse
The opposite conversion, from a string to an integer, is done with the
parse method. Since the text may not be a valid number,
parse returns a Result.
fn main() {
let s = 42.to_string();
let n: i32 = s.parse().unwrap();
println!("{}", n + 1);
for text in ["17", " 23 ", "4.5", "abc", "300"] {
match text.trim().parse::<u8>() {
Ok(v) => println!("{text:?} -> {v}"),
Err(e) => println!("{text:?} -> error: {e}"),
}
}
let hex = i64::from_str_radix("ff", 16).unwrap();
println!("{hex}");
}
We convert a number to a string and back, and then parse several
strings into u8 values.
let n: i32 = s.parse().unwrap();
The target type is given by the type annotation of the variable.
match text.trim().parse::<u8>() {
Ok(v) => println!("{text:?} -> {v}"),
Err(e) => println!("{text:?} -> error: {e}"),
}
Here the target type is given with the turbofish. The parse
method does not accept surrounding whitespace, so we call trim
first. A decimal number, a word, and a value too large for u8
produce errors.
let hex = i64::from_str_radix("ff", 16).unwrap();
The from_str_radix function parses a number in the given
base.
$ cargo run -q 43 "17" -> 17 " 23 " -> 23 "4.5" -> error: invalid digit found in string "abc" -> error: invalid digit found in string "300" -> error: number too large to fit in target type 255
Digits and characters
A single digit can be converted to a char and back.
fn main() {
let d = 7;
let c = char::from_digit(d, 10).unwrap();
println!("{c}");
let digits: Vec<u32> = 2026.to_string().chars().map(|c| c.to_digit(10).unwrap()).collect();
println!("{digits:?}");
}
We convert a digit to a character and split a number into its digits.
let c = char::from_digit(d, 10).unwrap();
The char::from_digit function returns the character for a digit
in the given base. It returns None if the value is not a valid
digit, for example 12 in base 10.
let digits: Vec<u32> = 2026.to_string().chars().map(|c| c.to_digit(10).unwrap()).collect();
We convert the number to a string, go over its characters, and turn each one
back into a number with to_digit.
$ cargo run -q 7 [2, 0, 2, 6]
Source
In this article we have converted integers to strings in Rust.
Author
List all Rust tutorials.