Is_numeric? or Another PHP dude trying to be a Rust chad!

Been writing and spending more and more of time in the Rust world. I've looped back to a project of mine that's still really noob'ish. Lot's of expect and unwrap! :slight_smile:

From the title, you can prolly tell that I spent a bit of time in the PHP world. When looking for info / an equivalent of is_numeric, I found this thread --> Check if string is numeric. The suggestion was to do this...

s.parse::().is_ok()

While this def get's the job done, I do have an issue here. My thinking is that I would like to check if the string I'm casting is numerical before even attempting. Perhaps this is my time in high level scripting languages speaking? I don't know.

Anyway, I wrote this to make me feel better. LOL

static NUM_CHECKLIST: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
fn is_numeric(test_string: &String) -> bool
    {
    for chr in test_string.chars()
        {
        if(!NUM_CHECKLIST.contains(&chr))
            { return false; }
        }

    true
    }

So how UN-idiomatic is this? To me, it feels like it would lead to slightly more code that is more obvious or explicit.

Thoughts? My flame retardent suit is on. I CAN TAKE IT! LOL

Two things:

  • If you want to check whether a string consists solely of ASCII decimal digits, you can simplify your code as follows:

    fn is_numeric(s: &str) -> bool {
        s.chars().all(|c| c.is_ascii_digit())
    }
    

    I would also recommend changing the name of this function to is_integer() or similar; I'm not familiar with PHP, but is_numeric() makes me think you also want to accept floating-point strings.

  • I'm not sure why you want to check whether the string is numeric before converting it. If your overall goal is just to convert the string to an integer, str::parse() will get the job done, and since it operates on a &str rather than consuming a String, you'll still have the original string available if needed if conversion fails.

9 Likes

I'd ask what you want to do with the result.

For example, is "999999999999999999999999999999999999999999" numeric? It's too big to fit in even a u128, so will fail to parse as any integer, but it's only numeric digits.

In general, in Rust it'll go better if you just run the thing and see if it worked. Don't check if it's a number then .parse().unwrap(), just call parse and look whether it worked.

10 Likes

Thanx guys! Your comments help for one big reason! I've been thinking in dynamically typed scripting level way for some time!

Sometimes it's hard to think beyond where you've been!

Thanx again!

Related:

This blog post was world view-changing for me. It uses Haskell examples, but Rust uses a similar philosophy in its API design.

Edit: I didn't remember just how Haskell-heavy this article was. I think it's worth a read even if you ignore the code snippets. But also here's some Haskell syntax:

function_name :: input_type -> output_type
function_name input1 = output1
function_name input2 = output2
fn function_name(input: input_type) -> output_type {
    match input {
        input1 => output1,
        input2 => output2,
    }
}
Maybe a  -- Option<a>
Just x   -- Some(x)
Nothing  -- None
6 Likes

Wow! Thanx!!!

Just wanted to respond as a follow up. Not like it matters to anyone, but any commentary is nice if you interested.

*) This is being used in a cli app that rotates desktop backgrounds. There is a config file and command line options. Therefore...
*) I want the user to enter 90 at the command line or in the config file as opposed to 'ninety', as an example.
*) @scottmcm made a fantastic point in his response concerning the length. There is now an upper limit.
*) @FZ shared an amazing article, but I think when the message from scottmcm is considered, I do need to validate the input somewhat before moving forward.
*) @jwodder | Dead on! php is_numeric will return true for string representations of floats. And thanx again for pointing me towards is_ascii_digit().

Thanx again!

Just wanted to throw in, it sounded to me like the "bunch of nines" case would be handled perfectly by s.parse(). Instead of manually checking yourself if the length is too long, delegate it to the standard library FromStr implementations. They will handle these in a standard way, based on the numeric type you parse it into.

(To put it another way, I read scottmcm's reply as an argument to not validate, instead just parse and handle the error appropriately)

8 Likes

Based on the given code, if another requirement is that the s only represents a number from 0 to 359, how do you integrate that?

That's an example of why parsing (and validating in the process) makes sense. Then you can check the result of the parse to do further validation.

1 Like