---
title: "Rust Enums, Matching, & Options API | CoderPad"
description: "One of the many features that highlights Rust’s capabilities is its handling of enums and matching."
canonical: "https://coderpad.io/blog/development/rust-enums-matching-options-api/"
last_modified: "2023-06-07 14:19:43"
---
[Back to blog](https://coderpad.io/blog/)

# Rust Enums, Matching, & Options API

April 16, 2021 6 Minute Read

By Corbin Crutchley

[Development](https://coderpad.io/blog/development/)![Rusty engine car part](https://coderpad.io/wp-content/uploads/2021/04/rusty-engine-by-water.jpg)

If you’ve been active in the programming community within the past few years, you’ve undoubtedly heard of [Rust](https://www.rust-lang.org/). Its technical foundation and vibrant community have proven themselves to be a good benchmark for quick language growth.

But what does Rust do that has garnered such a positive response from the community? Not only does Rust provide a great deal of memory safety (something that’s rare in low-level languages in the past), but also includes powerful features that make development much nicer.

One of the many features that highlights Rust’s capabilities is its handling of enums and matching.

## Enums

Like many languages with strict typings, Rust [has an enum feature](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html). To declare an enum is simple enough, start with`pub enum`and name the values.

```
pub enum CodeLang {
    Rust,
    JavaScript,
    Swift,
    Kotlin,
    // ...
}Code language: Rust (rust)
```

To create a variable with the type of that enum, you can use the name of the enum with the value:

```
fn main() {
   let lang = CodeLang::Rust;
}Code language: Rust (rust)
```

Likewise, you can use the`enum`as a type in places like function params. Let’s say that you want to detect which version of a programming language supported in CoderPad. We’ll start by hard-coding the version of Rust:

```
fn get_version(_lang: CodeLang) -> &'static str {
   return "1.46";
}Code language: Rust (rust)
```

While this code works, it’s not very functional. If you pass in “CodeLang::JavaScript”, the version number isn’t correct. Let’s take a look at how we can fix that in the next section.

## Matching

While you could use`if`statements to detect which enum is passed in, like so:

```
fn get_version(lang: CodeLang) -> &'static str {
    if let CodeLang::Rust = lang {
        return "1.46";
    }

    if let CodeLang::JavaScript = lang {
        return "2021";
    }

    return ""
}

fn main() {
    let lang = CodeLang::Rust;

    let ver = get_version(lang);

    println!("Version {}", ver);
}Code language: Rust (rust)
```

This easily becomes unwieldy when dealing with more than one or two values in the enum. This is where Rust’s`match`operator comes into play. Let’s match the variable with all of the existing values in the enum:

```
fn get_version(lang: CodeLang) -> &'static str {
   match lang {
       CodeLang::Rust => "1.46",
       CodeLang::JavaScript => "2021",
       CodeLang::Swift => "5.3",
       CodeLang::Python => "3.8"
   }
}Code language: Rust (rust)
```

If you’re familiar with a programming language that has a feature similar to “ [switch/case](https://www.tutorialspoint.com/cprogramming/switch_statement_in_c.htm) ”, this example is a close approximation of that functionality. However, as you’ll soon see,`match`in Rust is significantly more powerful than most implementations of switch/case.

## Pattern Matching

While most implementations of`switch/case`only allow simple primitives matching, such as strings or numbers, Rust’s`match`allows you to have more granular control over what is matched and how. For example, you can match anything that isn’t matched otherwise using the`_`identifier:

```
fn get_version(lang: CodeLang) -> &'static str {
   match lang {
       CodeLang::Rust => "1.46",
       _ => "Unknown version"
   }
}Code language: Rust (rust)
```

You are also able to match more than a single value at a time. In this example, we’re doing a check on versions for more than one programming language at a time.

```
fn get_version<'a>(lang: CodeLang, other_lang: CodeLang) -> (&'a str, &'a str) {
   match (lang, other_lang) {
       (CodeLang::Rust, CodeLang::Python) => ("1.46", "3.8"),
       _ => ("Unknown", "Unknown")
   }
}Code language: Rust (rust)
```

This shows some of the power of`match`. However, there’s more that you’re able to do with enums.

## Value Storage

Not only are enums values within themselves, but you can also store values within enums to be accessed later.

For example, CoderPad supports two different versions of Python. However, instead of creating a`CodeLang::Python`and`CoderLang::Python2`enum values, we can use one value and store the major version within.

```
pub enum CodeLang {
   Rust,
   JavaScript,
   Swift,
   Python(u8),
   // ...
}

fn main() {
   let python2 = CodeLang::Python(2);

   let pythonVer = get_version(python2);
}Code language: Rust (rust)
```

We’re able to expand our`if let`expression from before to access the value within:

```
if let CodeLang::Python(ver) = python2 {
    println!("Python version is {}", ver);
}Code language: Rust (rust)
```

However, just as before, we’re able to leverage`match`to unpack the value within the enum:

```
fn get_version(lang: CodeLang) -> &'static str {
   match lang {
       CodeLang::Rust => "1.46",
       CodeLang::JavaScript => "2021",
       CodeLang::Python(ver) => {
           if ver == 3 { "3.8" } else { "2.7" }
       },
        _ => "Unknown"
   }
}Code language: Rust (rust)
```

Not all enums need to be manually set, however! Rust has some enums built-in to the language, ready for use.

## Option Enum

While we’re currently returning the string`”Unknown”`as a version, that’s not ideal. Namely, we’d have to do a string comparison to check if we’re returning a known version or not, rather than having a value dedicated to a lack of value.
This is where Rust’s`Option`enum comes into play.`Option<T>`describes a data type that either has`Some(data)`or`None`to speak of.

For example, we can rewrite the above function to:

```
fn get_version<'a>(lang: CodeLang) -> Option<&'a str> {
   match lang {
       CodeLang::Rust => Some("1.46"),
       CodeLang::JavaScript => Some("2021"),
       CodeLang::Python(ver) => {
           if ver == 3 { Some("3.8") } else { Some("2.7") }
       },
        _ => None
   }
}Code language: Rust (rust)
```

By doing this, we can make our logic more representative and check if a value is`None`

```
fn main() {
    let swift_version = get_version(CodeLang::Swift);

    if let None = swift_version {
        println!("We could not find a valid version of your tool");
        return;
    }
}Code language: Rust (rust)
```

Finally, we can of course use`match`to migrate from an`if`to check when values are set:

```
fn main() {
    let code_version = get_version(CodeLang::Rust);

    match code_version {
        Some(val) => {
            println!("Your version is {}", val);
        },
        None => {
            println!("We could not find a valid version of your tool");
            return;
        }
    }
}Code language: Rust (rust)
```

## Operators

While the above code functions as intended, if we add more conditional logic, we may find ourselves wanting to make abstractions. Let’s look at some of these abstractions Rust provides for us

### Map Operator

What if we wanted to convert`rust_version`to a string, but wanted to handle edge-cases where`None`was present.

You might write something like this:

```
fn main() {
    let rust_version = get_version(CodeLang::Rust);

    let version_str = match rust_version {
        Some(val) => {
            Some(format!("Your version is {}", val))
        },
        None => None
    };

    if let Some(val) = version_str {
        println!("{}", val);
        return;
    }
}Code language: Rust (rust)
```

This`match`of taking`Some`and mapping it to a new value and leaving`None`s to resolve as`None`still is baked into the Option enum as a method called`.map`:

```
fn main() {
    let rust_version = get_version(CodeLang::Rust);

    let version_str = rust_version.map(|val| {
      format!("Your version is {}", val)
    });

    if let Some(val) = version_str {
        println!("{}", val);
        return;
    }
}Code language: Rust (rust)
```

How close is the implementation of`.map`to what we were doing before? Let’s take a look at [Rust’s source code implementation of`.map`](https://github.com/rust-lang/rust/blob/8dc0ae24bcafeb52259ae20fcad29185acf31fcc/library/core/src/option.rs#L485-L490):

```
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
   match self {
       Some(x) => Some(f(x)),
       None => None,
   }
}Code language: Rust (rust)
```

As you can see, we matched our implementation very similarly, matching`Some`to another`Some`and`None`to another`None`

### And Then Operator

While the automatic wrapping of the`.map`function return value into a`Some`can be useful in most instances, there may be times where you want to conditionally make something inside the`map`

Let’s say that we only want version numbers that contain a dot (indicating there’s a minor version). We could do something like this:

```
fn main() {
    let rust_version = get_version(CodeLang::JavaScript);

    let version_str = match rust_version {
        Some(val) => {
            if val.contains(".") {
                Some(format!("Your version is {}", val))
            } else {
                None
            }
        },
        None => None
    };

    if let Some(val) = version_str {
        println!("{}", val);
        return;
    }
}Code language: Rust (rust)
```

Which we can rewrite using Rust’s`and_then`operator:

```
fn main() {
    let rust_version = get_version(CodeLang::JavaScript);

    let version_str = rust_version.and_then(|val| {
        if val.contains(".") {
            Some(format!("Your version is {}", val))
        } else {
            None
        }
    });

    if let Some(val) = version_str {
        println!("{}", val);
        return;
    }
}Code language: Rust (rust)
```

If we look at [Rust’s source code for the operator](https://github.com/rust-lang/rust/blob/8dc0ae24bcafeb52259ae20fcad29185acf31fcc/library/core/src/option.rs#L722-L727), we can see the similarity to the`.map`implementation, simply without wrapping`fn`in`Some`:

```
pub fn and_then<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<U> {
        match self {
            Some(x) => f(x),
            None => None,
        }
    }Code language: Rust (rust)
```

## Putting it Together

Now that we’re familiar with the Option enum, operators, and pattern matching let’s put it all together!

Let’s start with the same`get_version`function baseline we’ve been using for a few examples:

```
use regex::Regex;

pub enum CodeLang {
   Rust,
   JavaScript,
   Swift,
   Python(u8),
   // ...
}

fn get_version<'a>(lang: CodeLang) -> Option<&'a str> {
   match lang {
       CodeLang::Rust => Some("1.46"),
       CodeLang::JavaScript => Some("2021"),
       CodeLang::Python(ver) => {
           if ver == 3 { Some("3.8") } else { Some("2.7") }
       },
        _ => None
   }
}

fn main() {
    let lang = CodeLang::JavaScript;

    let lang_version = get_version(lang);
}Code language: Rust (rust)
```

Given this baseline, let’s build a semver checker. Given a coding language, tell us what the major and minor versions of that language are.

For example, Rust (1.46) would return “ Major: 1. Minor: 46 ”, while JavaScript (2021) would return “Major: 2021. Minor: 0 ”

We’ll do this check using a Regex that parses any dots in the version string.

```
(\d+)(?:\.(\d+))?Code language: Rust (rust)
```

This regex will match the first capture group as anything before the first period, then optionally provide a second capture if there is a period, matching anything after that period. Let’s add that Regex and the captures in our`main`function:

```
let version_regex = Regex::new(r"(\d+)(?:\.(\d+))?").unwrap();

let version_matches = lang_version.and_then(|version_str| {
    return version_regex.captures(version_str);
});Code language: Rust (rust)
```

In the code sample above, we’re using`and_then`in order to flatten`captures`into a single-layer`Option`enum – seeing as`lang_version`is an Option itself and`captures`returns an Option as well.

While`.captures`sounds like it should return an array of the capture strings, in reality it returns [a structure with various methods and properties](https://docs.rs/regex/1.1.9/regex/struct.Captures.html). To get the strings for each of these values, we’ll use`version_matches.map`to get both of these capture group strings:

```
let major_minor_captures = version_matches
        .map(|caps| {
            (
                caps.get(1).map(|m| m.as_str()),
                caps.get(2).map(|m| m.as_str()),
            )
        });Code language: Rust (rust)
```

While we’d expect capture group 1 to always provide a value (given our input), we’d see “None” returned in capture group 2 if there’s no period (like with JavaScript’s version number of “2021”). Because of this, there are instances where`caps.get(2)`may be`None`. As such, we want to make sure to get a`0`in the place of`None`and convert the`Some<&str>, Option<&str>`into`Some<&str, &str>`. To do this, we’ll use`and_then`and a`match`:

```
let major_minor = major_minor_captures
    .and_then(|(first_opt, second_opt)| {
        match (first_opt, second_opt) {
            (Some(major), Some(minor)) => Some((major, minor)),
            (Some(major), None) => Some((major, "0")),
            _ => None,
        }
    });Code language: Rust (rust)
```

Finally, we can use an`if let`to deconstruct the values and print the major and minor versions:

```
if let Some((first, second)) = major_minor {
    println!("Major: {}. Minor: {}", first, second);
}Code language: Rust (rust)
```

The final version of the project should look something like this:

```
use regex::Regex;

pub enum CodeLang {
   Rust,
   JavaScript,
   Swift,
   Python(u8),
   // ...
}

fn get_version<'a>(lang: CodeLang) -> Option<&'a str> {
   match lang {
       CodeLang::Rust => Some("1.46"),
       CodeLang::JavaScript => Some("2021"),
       CodeLang::Python(ver) => {
           if ver == 3 { Some("3.8") } else { Some("2.7") }
       },
        _ => None
   }
}

fn main() {
    let lang = CodeLang::JavaScript;

    let lang_version = get_version(lang);

    let version_regex = Regex::new(r"(\d+)(?:\.(\d+))?").unwrap();

    let version_matches = lang_version.and_then(|version_str| {
        return version_regex.captures(version_str);
    });

    let major_minor_captures = version_matches
        .map(|caps| {
            (
                caps.get(1).map(|m| m.as_str()),
                caps.get(2).map(|m| m.as_str()),
            )
        });

    let major_minor = major_minor_captures
        .and_then(|(first_opt, second_opt)| {
            match (first_opt, second_opt) {
                (Some(major), Some(minor)) => Some((major, minor)),
                (Some(major), None) => Some((major, "0")),
                _ => None,
            }
        });

    if let Some((first, second)) = major_minor {
        println!("Major: {}. Minor: {}", first, second);
    }
}Code language: Rust (rust)
```

## Conclusion & Challenge

All of these features are used regularly in Rust applications: enums, matching, option operators. We hope that you can take these features and utilize them in your applications along your journey to learn Rust.

Let’s close with a challenge. If you get stuck anywhere along the way or have comments/questions about this article, you can join our [public chat community where we talk about general coding topics as well as interviewing](http://bit.ly/coderpad-slack).

Let’s say that we have the “patch” version of a software tracked. We want to expand the logic of our code to support checking “5.1.2” and return “2” as the “patch” version. Given the modified regex to support three optional capture groups:

```
(\d+)(?:\.(\d+))?(?:\.(\d+))?Code language: Rust (rust)
```

How can you modify the code below to support the match version being listed out properly?

You’ll know the code is working when you’re able to output the following:

```
Major: 2021. Minor: 0, Patch: 0
Major: 1. Minor: 46, Patch: 0
Major: 5. Minor: 1, Patch: 2Code language: Rust (rust)
```

## Subscribe for more CoderPad insights

## Trending Articles

Previous slide Next slide [Development

## What is Prompt Engineering?

Introducing AI tools such as ChatGPT and Bard helps increase the efficiency of tasks such as data generation, code generation and completion, text summarization, etc. How were these AI tools trained to answer almost every question you ask them? The answer, prompt engineering, is discussed in this blog post.

Read the post](https://coderpad.io/blog/development/what-is-prompt-engineering/) [Development

## A Guide To Using React’s useCallback() Hook

Are you looking to optimize your React applications? Have you heard of the useCallback hook? useCallback is a powerful React hook that helps you optimize your application by preventing unnecessary re-renders. In this post, we'll dive into React's useCallback() hook, define referential equality and callback functions and how to tie it all together.

Read the post](https://coderpad.io/blog/development/a-guide-to-using-reacts-usecallback-hook/) [Development

## MySQL vs. PostgreSQL: How Do They Compare?

A database is a convenient tool that allows users to access information, maintain records, and apply commonly used operations, such as insertion, modification, deletion, and data organization. Learn about MySQL vs PostgreSQL, their features, advantages, and disadvantages. Finally, check out which one is the best choice for you.

Read the post](https://coderpad.io/blog/development/mysql-vs-postgresql-how-do-they-compare/) [Development

## PostgreSQL LIKE Operator: A Detailed Guide

When using databases, searching for values that match a given pattern is a familiar necessity. Learn how you can retrieve entries with queries including a particular string using Postgres LIKE operator.

Read the post](https://coderpad.io/blog/development/postgresql-like-operator-a-detailed-guide/) [Development

## How To Use DataProvider In TestNG With Examples

When it comes to testing there are many tools and techniques available. In this post, we'll discuss how to use DataProvider with TestNG.

Read the post](https://coderpad.io/blog/development/how-to-use-dataprovider-in-testng-with-examples/) [Development

## How To Get Up And Running With Django Migrations: A Guide

Django migrations are a way of handling the application of changes to a database schema. By following the steps outlined in this guide, you can easily set up and use migrations in your Django projects.

Read the post](https://coderpad.io/blog/development/how-to-get-up-and-running-with-django-migrations-a-guide/) [Development

## Writing A Parameterized Test In JUnit With Examples

A JUnit parameterized test allows you to decouple data from structure, generating cleaner and more reliable tests. Learn more in this guide.

Read the post](https://coderpad.io/blog/development/writing-a-parameterized-test-in-junit-with-examples/) [Development

## How To Use Assert In JUnit With Examples

The JUnit Assert class is what you use to write unit testing assertions in JUnit. Learn more about it in this guide, full of examples.

Read the post](https://coderpad.io/blog/development/how-to-use-assert-in-junit-with-examples/) [Development

## A Simple Guide To TypeORM: What It Is And How To Use It

Considering current languages have object-oriented programming features, most database management systems are based on the relational model, with tables instead of objects. For these reasons, mapping relational objects is very important in development. In this article, you will learn what TypeORM, one of the main tools used today, is.

Read the post](https://coderpad.io/blog/development/a-simple-guide-to-typeorm-what-it-is-and-how-to-use-it/) [Development

## Using PreparedStatement in JDBC With Examples

In this post, we'll see why and how to work with JDBC and PreparedStatement. We will also learn along with examples using code.

Read the post](https://coderpad.io/blog/development/using-preparedstatement-in-jdbc-with-examples/) [Development

## React vs React Native: The Differences Explained

React and React Native are similar in that they both allow web developers to build applications based on the unique concept of components. But the two frameworks do this in radically different ways. In this post, we cover the key differences between react and react native frameworks, implementation, and which is an appropriate choice.

Read the post](https://coderpad.io/blog/development/react-vs-react-native-the-differences-explained/) [Development

## Logging in Django

The most challenging part of fixing a problem is identifying it. Unfortunately, this statement is exceptionally true when it comes to debugging. Learn how to use the power of logging into your Django applications to improve your debug process in this article.

Read the post](https://coderpad.io/blog/development/logging-in-django/)

## Hire with confidence.

See why engineering teams trust CoderPad to evaluate developers with realistic, AI-ready hiring workflows.

[Request a demo](https://coderpad.io/sales/)
