Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I allow my program to continue when a regex doesn't match?

Tags:

regex

rust

I want to use the regex crate and capture numbers from a string.

let input = "abcd123efg";
let re = Regex::new(r"([0-9]+)").unwrap();
let cap = re.captures(e).unwrap().get(1).unwrap().as_str();
println!("{}", cap);

It worked if numbers exist in input, but if numbers don't exist in input I get the following error:

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value'

I want my program continue if the regex doesn't match. How can I handle this error?

like image 322
Saeed M. Avatar asked Dec 19 '22 08:12

Saeed M.


2 Answers

You probably want to (re-)read the chapter on "Error Handling" in the Rust book. Error handling in Rust is mostly done via the types Result<T, E> and Option<T>, both representing an optional value of type T with Result<T, E> carrying additional information about the absence of the main value.

You are calling unwrap() on each Option or Result you encounter. unwrap() is a method saying: "if there is no value of type T, let the program explode (panic)". You only want to call unwrap() if an absence of a value is not expected and thus would be a bug! (NB: actually, the unwrap() in your second line is a perfectly reasonable use!)

But you use unwrap() incorrectly twice: on the result of captures() and on the result of get(1). Let's tackle captures() first; it returns an Option<_> and the docs say:

If no match is found, then None is returned.

In most cases, the input string not matching the regex is to be expected, thus we should deal with it. We could either just match the Option (the standard way to deal with those possible errors, see the Rust book chapter) or we could use Regex::is_match() before, to check if the string matches.

Next up: get(1). Again, the docs tell us:

Returns the match associated with the capture group at index i. If i does not correspond to a capture group, or if the capture group did not participate in the match, then None is returned.

But this time, we don't have to deal with that. Why? Our regex (([0-9]+)) is constant and we know that the capture group exists and encloses the whole regex. Thus we can rule out both possible situations that would lead to a None. This means we can unwrap(), because we don't expect the absence of a value.

The resulting code could look like this:

let input = "abcd123efg";
let re = Regex::new(r"([0-9]+)").unwrap();
match re.captures(e) {
    Some(caps) => {
        let cap = caps.get(1).unwrap().as_str();
        println!("{}", cap);
    }
    None => {
        // The regex did not match. Deal with it here!
    }
}
like image 50
Lukas Kalbertodt Avatar answered May 20 '23 04:05

Lukas Kalbertodt


You can either check with is_match or just use the return type of captures(e) to check it (it's an Option<Captures<'t>>) instead of unwrapping it, by using a match (see this how to handle options).

like image 21
hellow Avatar answered May 20 '23 05:05

hellow