Dynamic type casting of return value?

In the following code, parse_int is intended to parse a string into a nonzero integer. If I always parse using usize, it works. If I try to indicate the type of integer (e.g., u32 vs i64), I cannot do the comparison in the guard. Is there any way to do what I want?

use std::error::Error;
use core::str::FromStr;

type MyResult<T> = Result<T, Box<dyn Error>>;

fn main() {
    println!("{:?}", parse_int("foo"));
    println!("{:?}", parse_int("3"));
    println!("{:?}", parse_int2::<u32>("foo"));
    println!("{:?}", parse_int2::<u32>("3"));
}

fn parse_int(val: &str) -> MyResult<usize> {
    match val.trim().parse::<usize>() {
        Ok(n) if n != 0 => Ok(n),
        _ => Err(From::from(val)),
    }
}

fn parse_int2<T: FromStr + std::cmp::PartialEq>(val: &str) -> MyResult<T> {
    match val.trim().parse::<T>() {
        Ok(n) if n != 0 => Ok(n),
        _ => Err(From::from(val)),
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:22:23
   |
20 | fn parse_int2<T: FromStr + std::cmp::PartialEq>(val: &str) -> MyResult<T> {
   |               - this type parameter
21 |     match val.trim().parse::<T>() {
22 |         Ok(n) if n != 0 => Ok(n),
   |                       ^ expected type parameter `T`, found integer
   |
   = note: expected type parameter `T`
                        found type `{integer}`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Not an answer, but you'd need to constrain with more traits (like those from the num crate) to get the T::zero() value.

You could also use the NonZeroNums from the standard library, they implement FromStr making your function redundant.

Yes, I'd found core::num::NonZeroUsize and that's very cool. I'd be happy to use that, but how can I use the T to return the n value as that particular type?

Ultimately you need to add another trait bound that somehow allows you to check whether it is zero. You could do this in many ways:

  • Use the num crate as already suggested.
  • Define your own trait with an is_zero method and implement it for the integer types.
  • Use a trait like From<u8> and convert an 0u8 into whatever T is. (doesn't work for T = i8)

Sorry, but could you explain a little more how to use the num crate for this?

Sure, you do this:

fn parse_int2<T: FromStr + num::Zero>(val: &str) -> MyResult<T> {
    match val.trim().parse::<T>() {
        Ok(n) if !n.is_zero() => Ok(n),
        _ => Err(From::from(val)),
    }
}

Here you are using that the num crate has a trait called Zero which allows you to check whether something is zero or not.

Wow. Mind blown! Thanks, Alice!