ZetCode

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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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.

main.rs
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

std::fmt module documentation

ToString trait

In this article we have converted integers to strings in Rust.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Rust tutorials.