Strings and Text Processing
String Parsing
The str::parse::<F>() method converts a string slice into any target type F that implements the std::str::FromStr trait, returning Result<F, F::Err>.
Signatures of str::parse and FromStr
// Method on `str`:
pub fn parse<F: FromStr>(&self) -> Result<F, F::Err>
// Trait in `std::str`:
pub trait FromStr: Sized {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
All primitive numeric types (i8..i128, u8..u128, isize, usize, f32, f64), bool, char, IpAddr, SocketAddr, and PathBuf implement FromStr:
use std::net::SocketAddr;
fn main() {
// 1. Type annotation on the binding:
let count: u32 = "42".parse().unwrap();
// 2. Turbofish syntax `.parse::<Type>()`:
let ratio = "3.14159".parse::<f64>().unwrap();
let addr = "127.0.0.1:8080".parse::<SocketAddr>().unwrap();
// Note: `.parse()` does NOT trim leading/trailing whitespace automatically:
let trimmed_num: i32 = " -99\n".trim().parse().unwrap();
println!("count={count}, ratio={ratio:.2}, addr={addr}, trimmed={trimmed_num}");
}
count=42, ratio=3.14, addr=127.0.0.1:8080, trimmed=-99
Parsing Integers in Hexadecimal, Binary, or Octal (from_str_radix)
str::parse::<u32>() always parses base-10 integers and rejects "0xFF". To parse an integer in base 2 through 36 (such as hexadecimal "FF" or binary "1010"), use the inherent T::from_str_radix(src, radix) method:
fn parse_hex(input: &str) -> Result<u32, std::num::ParseIntError> {
let hex_digits = input.strip_prefix("0x").unwrap_or(input);
u32::from_str_radix(hex_digits, 16)
}
fn main() {
println!("0x1A3F -> {:?}", parse_hex("0x1A3F"));
println!("FF -> {:?}", parse_hex("FF"));
}
0x1A3F -> Ok(6719)
FF -> Ok(255)
Implementing FromStr for a Custom Type
Implementing FromStr enables .parse() on your own structs and enums:
use std::str::FromStr;
#[derive(Debug, PartialEq, Eq)]
struct SemVer {
major: u32,
minor: u32,
patch: u32,
}
#[derive(Debug)]
struct ParseSemVerError(&'static str);
impl FromStr for SemVer {
type Err = ParseSemVerError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split('.');
let mut next_num = || {
parts
.next()
.ok_or(ParseSemVerError("missing version component"))?
.parse::<u32>()
.map_err(|_| ParseSemVerError("invalid integer component"))
};
let major = next_num()?;
let minor = next_num()?;
let patch = next_num()?;
if parts.next().is_some() {
return Err(ParseSemVerError("too many version components"));
}
Ok(SemVer { major, minor, patch })
}
}
fn main() {
let v: SemVer = "1.85.0".parse().unwrap();
let bad = "1.85".parse::<SemVer>();
println!("Parsed: {v:?}");
println!("Invalid: {bad:?}");
}
Parsed: SemVer { major: 1, minor: 85, patch: 0 }
Invalid: Err(ParseSemVerError("missing version component"))