RustArena

Step 3 of 7

FromStr: parse into your own type

"42".parse::<i32>() works because i32 implements the trait std::str::FromStr. Implement it for your own type and .parse() works for it too. This is the idiomatic way to turn text (config files, command-line arguments, user input) into a value.

use std::str::FromStr;

#[derive(Debug)]
struct Setting {
    key: String,
    value: i64,
}

impl FromStr for Setting {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (key, value) = s.split_once('=').ok_or(format!("missing '=' in {s:?}"))?;
        let key = key.trim();
        if key.is_empty() {
            return Err(String::from("empty key"));
        }
        let value = value.trim().parse::<i64>().map_err(|_| format!("bad number {value:?}"))?;
        Ok(Setting { key: key.to_string(), value })
    }
}

fn main() {
    let s: Setting = "volume = 7".parse().unwrap();
    println!("{} -> {}", s.key, s.value);
    println!("{:?}", "volume".parse::<Setting>());
    println!("{:?}", "=3".parse::<Setting>());
    println!("{:?}", "lives=lots".parse::<Setting>());
}
volume -> 7
Err("missing '=' in \"volume\"")
Err("empty key")
Err("bad number \"lots\"")

Piece by piece:

Why not a fn parse_setting(s: &str)?

It would work, but FromStr plugs into the rest of the ecosystem. Code that reads "any value that can be parsed" (T: FromStr) works with your type automatically: command-line parsers like clap, config loaders, and your own generic helpers.

Common mistakes

Your task

Implement FromStr for Dimensions so that "3x4".parse::<Dimensions>() gives Ok(Dimensions { width: 3, height: 4 }). Allow spaces around the numbers (" 10 x 20 "). Return an Err (any String message) if there's no x or if either side isn't a whole number.

Challenge 2

Implement FromStr for Color so that web colors like "#ff8800" parse into Color { r: 255, g: 136, b: 0 }. The text must be exactly 7 characters: a # followed by 6 hex digits (upper or lower case). Anything else is an Err.

u8::from_str_radix(text, 16) parses a hex number such as "ff" into 255 (it returns a Result, like parse). Slice out the pairs with &s[1..3], &s[3..5] and &s[5..7].

Challenge 3

A robot reads commands, one per line from stdin. Implement FromStr for Command:

Then main (already written) parses every line and prints what the robot does, or error: <message>.

Previous: TryFrom and TryInto Next: Operator overloading