Regular expressions in Rust
Tags: rust, regex, guide
Published: 2021-06-18 03:20:59

In many popular languages such as Javascript or Python, using regular expressions is simple.  Well.. *mostly* simple. 

For example, let's say I have this inscrutable JS string: 
`let a = "123bbasfsdf23asd2021-06-17";`

For reasons known only to me, I desire to find numbers that are at most 3 digits long - specifically, I want to find the number "23."  

The solution?  A regular expression.  In JavaScript syntax, `\d` is a number, and putting `{1,3}` after the digit marker means we search for any group of 1 to 3 adjacent digits

```js
let regex = /\d{1,3}/gi
let a = "123bbasfsdf23asd2021-06-17"
a.match(regex)

     ==>["123", "23", "202", "1", "06", "17"]
``` 

That was easy!  Now let's try to reproduce this in Rust.  As Rust is a lower-level language focused on much different things than Javascript, the process is much different.

First, we have to use the [`regex`](https://docs.rs/regex) crate, which thankfully uses similar expression syntax to JavaScript.  So to recreate our previous example, we first have to import the crate by both putting it into `Cargo.toml` under `[dependencies]` and by beginning our code with a `use` statement.  So our beginning code is fairly simple:

```rust
use regex::Regex;

fn main() {
    let regex = Regex::new(r"\d{3}").unwrap();
    let a: &str = "123bbasfsdf23asd2021-06-17";
}
```
Some of this might not make sense:

The [`r`](https://doc.rust-lang.org/reference/tokens.html#raw-string-literals) in front of our regular expression makes it so we don't have to spam backslashes to make our strings valid.  Especially useful for backslash-heavy regexes! 

The `.unwrap()` comes from [`Option<T>`](https://doc.rust-lang.org/stable/book/ch06-01-defining-an-enum.html?highlight=option#the-option-enum-and-its-advantages-over-null-values) which basically replaces `null` or `undefined` with `None`.  I imagine these regexes are Options because there are cases (such as in user input) where the regex can indeed be empty.  But, because we are hardcoding the value of the regular expression, we can safely call `unwrap`, which exposes the string value of the regular expression and allows us to do stuff with it without having to do pattern matching or whatever.  

Having written these simple lines, our problems begin.  In the [documentation for Regex](https://docs.rs/regex/1.5.4/regex/struct.Regex.html), there's no `match` function.  There are `find` and `capture`, but each of these only return the first leftmost match - insufficient for my aim of finding the second match.  

After lots of thinking and help from the Rust community, I finally decided on `find_iter`, which returns an `Iterator` composed of all the matches.  Does it work?  Note that I used the regex directly, skipping assigning it to a variable.  

```rust
use regex::Regex;

fn main() {
    let a: &str = "123bbasfsdf23asd2021-06-17";
    for cap in Regex::new(r"\d{1,3}").unwrap().find_iter(a) {
        println!("{:#?}", cap);
    }
}


==> Match {
    text: "123bbasfsdf23asd2021-06-17",
    start: 0,
    end: 3,
}
Match {
    text: "123bbasfsdf23asd2021-06-17",
    start: 11,
    end: 13,
}
Match {
    text: "123bbasfsdf23asd2021-06-17",
    start: 16,
    end: 19,
}
Match {
    text: "123bbasfsdf23asd2021-06-17",
    start: 19,
    end: 20,
// --snip 
```

So yes, this works.  But it's also practically useless - trying to log `cap.start` or `cap.end` throws an error about "private fields."  After even more thinking, scrolling through documentation and conversing with friendly Discord members, I finally found the [`as_str`](https://docs.rs/regex/1.5.4/regex/struct.Match.html#method.as_str) method.  

Side note, putting `:?` in the curly braces of `println` lets you log stuff that empty braces can not format, while `:#?` prettyprints it.  [Read more about it](https://doc.rust-lang.org/std/fmt/) - its cool! 

```rust
use regex::Regex;

fn main() {
    let a: &str = "123bbasfsdf23asd2021-06-17";
    for cap in Regex::new(r"\d{1,3}").unwrap().find_iter(a) {
        println!("{:#?}", cap.as_str());
    }
}

=> "123"
"23"
"202"
"1"
"06"
"17"
```
Yay!!  Now I have my clean, useable outputs.  But how do I turn this into an array?  


In Rust, an [`Iterator`](https://doc.rust-lang.org/rust-by-example/trait/iter.html) is essentially a fancy array that makes looping and recursive functions easier.  As vectors (as arrays are named in Rust) can be easily turned into iterators through `into_iter`, iterators can be turned back into vectors through `collect`.

However, just running `Regex::new(r"\d{1,3}").unwrap().find_iter(a).collect()` doesn't work - not only do we have to write some type annotations, we get an error that we can't collapse a match iterator into a clean string vector.  

The solution?  Use the incredible `map` function (which every developer should know about!) and apply `as_str` to every item of the iterator.  Slap on a type annotation (as Rust requires) and some random borrowing, and voila:

```rust
let my_captures: Vec<&str> = (Regex::new(r"\d{1,3}").unwrap().find_iter(a).map(|x| x.as_str()).collect());
println!("{:?}", my_captures);
     => ["123", "23", "202", "1", "06", "17"]
```

Great!  Now use bracket notation: `my_captures[1]`, and you're done!  Try it yourself [here](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2691c4ff74771c9b2d0d749cdeba3d3c).


Hopefully this article was helpful to you - I spent over an hour banging my head against documentation and Discord to solve this.  Thanks for reading, and good luck!