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:
sowns 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.
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:
textis a&str.parse()tries to convert iti32tells Rust which numeric type you wantunwrap()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:
textis aString.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:
-
let text = String::from("256");- A
Stringis created. - It owns the text
"256".
- A
-
let number: i32 = text.parse().unwrap();.parse()is called ontext.- Rust borrows the contents of
textas&str. - It sees that you want an
i32. - It tries to convert the characters
2,5, and6into the integer256. - Parsing succeeds, so the result is
Ok(256). unwrap()extracts256fromOk(256).
-
println!("{}", number);- Prints
256.
- Prints
Now look at a failing example:
fn main() {
let text = String::from("hello");
let number: Result<i32, _> = text.parse();
println!("{:?}", number);
}
What happens:
textcontains"hello"..parse()tries to read it as ani32."hello"is not a valid integer.- Rust returns
Err(...)instead of crashing. - 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.
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 numberunwrap()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:
i32i64u32usize
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
| Concept | What it means | Common use |
|---|---|---|
String | Owned, growable text | Store and modify text |
&str | Borrowed view into text | Read text without ownership |
.parse::<i32>() | Convert text into a specific type | Parsing numbers from input |
unwrap() | Extract success value or panic | Quick demos, not ideal for production |
match on Result | Handle success and failure explicitly | Real applications |
String vs &str
| Feature | String | &str |
|---|---|---|
| Owns data | Yes | No |
| Can grow/change | Yes | No |
| Cheap to borrow | Yes | Not needed |
| Can be parsed | Yes, via borrowing | Yes |
parse() vs older from_str() style
| Approach | Status | Example |
|---|---|---|
from_str::<int>(...) | Old Rust style | Pre-1.0 code |
"42".parse::<i32>() | Modern Rust style | Recommended |
unwrap() vs explicit error handling
| Approach | Advantage | Disadvantage |
|---|---|---|
unwrap() | Short and simple | Panics on invalid input |
match | Safe and explicit | More verbose |
? operator | Clean error propagation | Requires a function returning Result |
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 integeri64— signed 64-bit integeru32— unsigned 32-bit integerusize— size/index type
Key rule
- Parsing reads text and returns
Result<T, E> Stringusually 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.
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.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.