parsingrust

Rust FromStr where the Type to parse into has Generic field and you won't know the type until after you attempt to parse


Lets assume you have a concrete type (that you created/own) you want to parse into but the type has a generic field, in this example, we'll say the possible types are only u8, u16 & u32 and let's just say that anything else is either incorrect or too big.

Assume the following:

I have the following code:

use std::str::FromStr;

#[derive(Debug)]
struct Test<T = u8> {
    field: T
}

impl<T> FromStr for Test<T> {
    type Err = std::num::ParseIntError;
    
    fn from_str(buff: &str) -> Result<Test<T>, Self::Err> {
        let attempt: Result<u8, Self::Err> = buff.parse();
        if let Ok(field) = attempt { return Ok(Test::<u8> { field }) };
        
        let attempt: Result<u16, Self::Err> = buff.parse();
        if let Ok(field) = attempt { return Ok(Test::<u16> { field }) };
        
        let attempt: Result<u32, Self::Err> = buff.parse();
        
        match attempt {
            Ok(field) => Ok(Test::<u32> { field }),
            Err(err) => Err(err)
        }
    } 
}



fn main() -> Result<(), std::num::ParseIntError> {
    let u_8 = "128";
    let u_16 = "475";
    let u_32 = "70000";
    
    let test_8: Test = u_8.parse()?;
    let test_16: Test = u_16.parse()?;
    let test_32: Test = u_32.parse()?;
    
    println!("t8: {test_8:?}");
    println!("t16: {test_16:?}");
    println!("t32: {test_32:?}");
    
    Ok(())
 
}

I get a ton of errors like:

= note: expected struct `Test<T>`
               found struct `Test<u16>`

which makes sense.

How do I make this work?

Bonus points

Why does Rust require me to do Test::<u8>, since I've specified the type above each return line why can't Rust just infer the T in Test<T>?

Is it because of Result<Self, Self::Err> and Self is Test<T>?


Solution

  • Suppose we have a function which parses your type. Either it will be generic, and you need to decide upon a variant when you call the function, or it will have a single return type. Either way, when you call this function it will have a single unambiguous return type.

    Now there are two ways to make this single type be a front for different possibilities: it could be an enum listing a pre-determined number of possibilities, or it could be something that impl's a specific trait (like Box<dyn Display>), for an extensible number of possibilities. Nothing else is possible.