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"))