Question

How to Convert a String to an Integer in Rust

Question

In Rust, converting a string slice to an integer can be done like this in older code:

let my_int = from_str::<int>(my_str);

If I have a String, one approach is to first get a string slice and then convert it:

let my_int = from_str::<int>(my_string.as_slice());

Is there a way to convert a String directly to an integer, or is borrowing it as a string slice the correct approach?

Short Answer

By the end of this page, you will understand how Rust converts text into numbers, why String and &str are treated differently, and how to use modern Rust methods like .parse() to convert both String and &str into integer types safely.

Concept

In Rust, parsing a number from text means reading characters like "42" and turning them into a numeric type like i32 or u64.

The main idea is that Rust separates:

  • Owned text: String
  • Borrowed text: &str

A String owns heap-allocated text. A &str is a view into text. Many parsing APIs work with &str because they only need to read the characters, not own them.

In modern Rust, the usual way to convert text to an integer is:

let n: i32 = "42".parse().unwrap();

This works because Rust provides parsing through the FromStr trait. Types like i32, u32, and usize implement this trait, so they know how to build themselves from text.

A String can also be parsed easily because method calls can automatically borrow it as &str when needed:

let s = String::from("42");
let n: i32 = s.parse().unwrap();

So in practice, yes, you can call .parse() directly on a String. Under the hood, Rust borrows the string data as &str.

This matters because parsing text input is common in real programs:

  • reading command-line arguments
  • handling form data
  • processing configuration files
  • decoding API input
  • validating user-entered values

Rust makes this explicit and safe by requiring parsing to return a Result, since not all text is a valid number.

Mental Model

Think of String as a book you own and &str as a page someone is reading from that book.

If you want to parse a number, the parser does not need to own the whole book. It just needs to read the page.

So when you write:

let s = String::from("123");
let n: i32 = s.parse().unwrap();

Rust is effectively saying:

  • s owns the text
  • the parser temporarily borrows that text
  • the parser reads it and produces an integer

You still keep ownership of the original String unless you move it elsewhere.

This is why parsing usually works through borrowing rather than copying.

Take Quiz

Syntax and Examples

Core syntax

In modern Rust, use .parse():

let n: i32 = "42".parse().unwrap();

Because .parse() returns a Result, you can also handle errors safely:

let n: Result<i32, _> = "42".parse();

Parsing a &str

fn main() {
    let text = "123";
    let number: i32 = text.parse().unwrap();
    println!("{}", number);
}

Explanation:

  • text is a &str
  • .parse() tries to convert it
  • i32 tells Rust which numeric type you want
  • unwrap() gets the value or panics if parsing fails

Parsing a String

fn main() {
    let text = String::from("123");
    let number: i32 = text.parse().unwrap();
    println!("{}", number);
}

Explanation:

  • text is a String
  • .parse() still works
  • Rust automatically borrows the string as &str

Parsing without unwrap()

fn main() {
    let text = String::from("abc");

    match text.parse::<i32>() {
        Ok(number) => println!("Parsed: {}", number),
        Err(error) => println!("Could not parse: {}", error),
    }
}

This is the safer approach for real applications.

Different integer types

fn main() {
    let a: i32 = "10".parse().unwrap();
    let b: u64 = "20".parse().unwrap();
    let c: usize = "30".parse().unwrap();

    println!("{} {} {}", a, b, c);
}

Use the type that matches your program's needs.

Step by Step Execution

Consider this example:

fn main() {
    let text = String::from("256");
    let number: i32 = text.parse().unwrap();
    println!("{}", number);
}

Step by step:

  1. let text = String::from("256");

    • A String is created.
    • It owns the text "256".
  2. let number: i32 = text.parse().unwrap();

    • .parse() is called on text.
    • Rust borrows the contents of text as &str.
    • It sees that you want an i32.
    • It tries to convert the characters 2, 5, and 6 into the integer 256.
    • Parsing succeeds, so the result is Ok(256).
    • unwrap() extracts 256 from Ok(256).
  3. println!("{}", number);

    • Prints 256.

Now look at a failing example:

fn main() {
    let text = String::from("hello");
    let number: Result<i32, _> = text.parse();
    println!("{:?}", number);
}

What happens:

  1. text contains "hello".
  2. .parse() tries to read it as an i32.
  3. "hello" is not a valid integer.
  4. Rust returns Err(...) instead of crashing.
  5. Printing the result shows the parse failure.

This is why parse() returns Result: conversion can fail.

Real World Use Cases

Parsing strings into integers appears in many practical Rust programs.

Command-line arguments

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    if args.len() > 1 {
        match args[1].parse::<i32>() {
            Ok(port) => println!("Port: {}", port),
            Err(_) => println!("Please provide a valid number"),
        }
    }
}

Used when reading values like ports, limits, or IDs.

User input

If a user types their age, quantity, or menu choice, the program receives text first. You must parse that text into a number before doing calculations.

Config files and environment variables

Environment variables are strings by default:

use std::env;

fn main() {
    let timeout = env::var("TIMEOUT")
        .ok()
        .and_then(|s| s.parse::<u64>().ok())
        .unwrap_or(30);

    println!("Timeout: {}", timeout);
}

API and web app input

Web requests often deliver data as strings. If a query parameter contains page=2, the app must convert "2" into a number before using it.

File and CSV processing

Data files often store numeric fields as text. Parsing turns them into numbers for sorting, filtering, and calculations.

Take Quiz

Real Codebase Usage

In real Rust codebases, developers usually combine parsing with validation and error handling.

Pattern: parse close to the input boundary

Convert text into a number as soon as you receive it.

fn parse_age(input: &str) -> Result<u8, std::num::ParseIntError> {
    input.parse::<u8>()
}

This keeps the rest of your code working with proper numeric types instead of raw strings.

Pattern: guard clauses for invalid input

fn set_retry_count(input: &str) -> Result<u32, String> {
    let count: u32 = input.parse().map_err(|_| "Invalid retry count".to_string())?;

    if count > 10 {
        return Err("Retry count must be 10 or less".to_string());
    }

    Ok(count)
}

First parse, then validate business rules.

Pattern: optional parsing with defaults

fn parse_limit(input: Option<String>) -> u32 {
    input
        .as_deref()
        .and_then(|s| s.parse::<u32>().ok())
        .unwrap_or(100)
}

Useful for config values and optional request parameters.

Pattern: collecting valid values from text data

fn main() {
    let items = vec!["10", "20", "abc", "40"];

    let numbers: Vec<i32> = items
        .into_iter()
        .filter_map(|s| s.parse::<i32>().ok())
        .collect();

    println!("{:?}", numbers);
}

This pattern is common in data processing.

Pattern: custom error messages

fn parse_user_id(input: &str) -> Result<i64, String> {
    input
        .parse::<i64>()
        .map_err(|_| format!("Invalid user ID: {}", input))
}

This makes debugging and user feedback clearer.

Common Mistakes

1. Forgetting that parsing can fail

Broken code:

fn main() {
    let text = "abc";
    let number: i32 = text.parse().unwrap();
    println!("{}", number);
}

Problem:

  • "abc" is not a number
  • unwrap() will panic

Better:

fn main() {
    let text = "abc";

    match text.parse::<i32>() {
        Ok(n) => println!("{}", n),
        Err(_) => println!("Invalid number"),
    }
}

2. Not specifying the target type

Broken code:

fn main() {
    let text = "42";
    let number = text.parse().unwrap();
}

Problem:

  • Rust may not know which numeric type you want

Fix it with a type annotation or turbofish:

let number: i32 = text.parse().unwrap();

or

let number = text.parse::<i32>().unwrap();

3. Confusing String and &str

Beginners often think they must manually convert String every time.

let text = String::from("42");
let number: i32 = text.parse().unwrap();

This already works. Rust borrows the string automatically.

4. Using outdated Rust APIs

Older code may show functions like from_str::<int>(...) or types like int.

Modern Rust uses fixed-width integer types such as:

  • i32
  • i64
  • u32
  • usize

And parsing is usually done with .parse().

5. Choosing the wrong integer type

Broken code:

let big_number: i32 = "5000000000".parse().unwrap();

Problem:

  • The value does not fit in i32

Use a larger type:

let big_number: i64 = "5000000000".parse().unwrap();

6. Ignoring whitespace issues

let text = " 42 ";
let number = text.parse::<i32>();

This may fail because of spaces. Trim first:

let number: i32 = text.trim().parse().unwrap();

Comparisons

ConceptWhat it meansCommon use
StringOwned, growable textStore and modify text
&strBorrowed view into textRead text without ownership
.parse::<i32>()Convert text into a specific typeParsing numbers from input
unwrap()Extract success value or panicQuick demos, not ideal for production
match on ResultHandle success and failure explicitlyReal applications

String vs &str

FeatureString&str
Owns dataYesNo
Can grow/changeYesNo
Cheap to borrowYesNot needed
Can be parsedYes, via borrowingYes

parse() vs older from_str() style

ApproachStatusExample
from_str::<int>(...)Old Rust stylePre-1.0 code
"42".parse::<i32>()Modern Rust styleRecommended

unwrap() vs explicit error handling

ApproachAdvantageDisadvantage
unwrap()Short and simplePanics on invalid input
matchSafe and explicitMore verbose
? operatorClean error propagationRequires a function returning Result
Take Quiz

Cheat Sheet

Quick reference

Parse a string slice

let n: i32 = "42".parse().unwrap();

Parse a String

let s = String::from("42");
let n: i32 = s.parse().unwrap();

Specify the type explicitly

let n = "42".parse::<u64>().unwrap();

Handle errors safely

match "42".parse::<i32>() {
    Ok(n) => println!("{}", n),
    Err(e) => println!("Error: {}", e),
}

Trim whitespace first

let n: i32 = " 42 ".trim().parse().unwrap();

Common integer types

  • i32 — signed 32-bit integer
  • i64 — signed 64-bit integer
  • u32 — unsigned 32-bit integer
  • usize — size/index type

Key rule

  • Parsing reads text and returns Result<T, E>
  • String usually parses by being borrowed as &str
  • Use .parse() in modern Rust, not old pre-1.0 APIs

FAQ

Can I parse a String directly in Rust?

Yes. You can call .parse() directly on a String, and Rust will borrow it as &str automatically.

What is the difference between String and &str when parsing?

String owns the text, while &str is a borrowed view. Parsing usually only needs a borrowed view, so both work.

Why does parse() return a Result?

Because not every string is a valid number. Rust forces you to handle possible failure safely.

How do I parse an integer without crashing?

Use match, if let, or the ? operator instead of unwrap().

What replaces from_str::<int>(...) in modern Rust?

Use .parse::<i32>(), .parse::<u64>(), or another specific integer type.

Why does Rust ask me to specify i32 or another type?

The parser needs to know what numeric type to create. Rust often cannot infer that automatically.

Will parsing work if the string has spaces?

Not always. Use .trim() before parsing if leading or trailing whitespace may be present.

Take Quiz

Mini Project

Description

Build a small Rust program that reads a list of text values, converts the valid ones into integers, and skips invalid entries. This demonstrates how parsing works with string slices, error handling, and collecting usable numeric data from messy input.

Goal

Create a program that parses text values into integers and reports which values were valid or invalid.

Requirements

  • Store several numeric and non-numeric text values in a collection.
  • Attempt to convert each value into an integer.
  • Print valid integers.
  • Print a message for invalid values.
  • Keep a final list of all successfully parsed integers.
Take Quiz

Keep learning

Related questions

View more