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:
impl FromStr for Test<u8> {}
etc for each type but then the compiler complains (Test requires generics) and then complains about conflicting implementation when I then add impl<T> FromStr for Test<T> {}
which makes sense since rust doesn't have specialisation yetu8
, u16
or u32
, I'm trying to make my example code more widely applicableI 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?
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>
?
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.