Autogenerated rust input parsing is not idiomatic and can be improved

Currently the autogenerated code uses a macro_rules macro for parsing input.

macro_rules! parse_input {
    ($x:expr, $t:ident) => ($x.trim().parse::<$t>().unwrap())
}

This is not very idiomatic and also uses a macro where a regular generic function with some trait bounds would suffice.

I propose the following parse_input function:

use std::fmt::Debug;
use std::str::FromStr;

fn parse_input<Input, Type>(input: Input) -> Type
where
    Type: FromStr,
    <Type as FromStr>::Err: Debug,
    Input: AsRef<str>,
 {
    input.as_ref().trim().parse().unwrap()
}

The usage would change from let name = parse_input!(input, TYPE); to let name: TYPE = parse_input(input);

Let’s take a line from the tutorial as an example:
Before: let dist_1 = parse_input!(input_line, i32); // distance to enemy 1
After: let dist_1: i32 = parse_input(input_line); // distance to enemy 1

Same code of parse_input but with explanations on what it does:

// Additional imports that are required for this to work
use std::fmt::Debug;
use std::str::FromStr;

fn parse_input<Input, Type>(input: Input) -> Type
where
    Type: FromStr, // .parse() that has been used in your macro is part of the FromStr trait, so this is required
    <Type as FromStr>::Err: Debug, // The .unwrap() panics with a message that contains the debug representation of the error, so the parsing error needs to implement Debug
    Input: AsRef<str>, // &str would probably have worked just fine for `input`, but this is more general and makes it work with both `String` and `&str` and even any other type that can be dereferenced as `&str`. This should also potentially make the code easier to generate automatically
 {
    // This is the same as in your macro, just with an additional `as_ref` to get access to the `&str` type.
    input.as_ref().trim().parse().unwrap()
}
2 Likes