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!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.