Menu Share

Rust Enums in Modern C++

by | Published on
category C / C++

Lately I see rust and the C and C++ languages being compared a lot in performance and capabilities. A lot of people support rust for its helpful compiler messages and its safety. And that is all true. I do like rust for these reasons. I have come across a lot of people that comment about rust enums being really powerful and I wanted to showcase how one can achieve the same functionality in C++.

Table of Contents

Introduction

What are Rust enums? Enumeration values are when you want to have a type that can only take a few predefinded values. For example if we look at an IP there are two standards for IPs – IPv4 and IPv6. They are written in a different way and you might want to distinguish in your code whenever you’re dealing with one or the other. Since they are both string values you would have to introduce a second value that defines the type. This value would be IpKind and it could hold values of IPv4 and IPv6. In rust defining this type would be like this:

enum IpKind {
    IPv4,
    IPv6,
}

Simple right? Well this is pretty much the same as defining an enumaration in C++:

enum IpKind {
    IPv4, 
    IPv6
};

It is literally the same. Where rust enums get a lot of hype though is having the ability to have values inside of them. So they actually become something like a struct. Rust will encapsulate this though and just give you access to the values inside.

enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));

This will allow you not only to instantiate a variable of that enum type but also store values in it like a struct. You could even go more advanced and name those variables. Like a struct is the key phrase here because this is essentially what rust will do. Those values will be stored in an union like structure with a type index that defines what the current enumeration type is.

C++’s approach to Rust Enums

In the latest C++ versions the language introduces the concept of a variant. You could #include <variant> and you will be able to use the std::variant<> template structure. This structure allows you to pass in multiple types and it will store an index that would keep track of which type is currently stored in the variant. Let’s takte the above example in mind and create an enum with three values – invalid, ipv4 and ipv6:

struct InvalidIP {};

struct IPv4 {
  std::string value;
};

struct IPv6 {
  std::string value;
};

using IPType = std::variant<InvalidIP, IPv4, IPv6>;

Then you could use the IPType to create variables and also test values:

IPType ipType = InvalidIP{};

if (IPv4 *ip = std::get_if<IPv4>(&ipType))
{
    std::cout << ip->value << std::endl;
}
else if (IPv6 *ip = std::get_if<IPv6>(&ipType))
{
    std::cout << ip->value << std::endl;
}
else if (InvalidIP *ip = std::get_if<InvalidIP>(&ipType))
{
    std::cout << "Invalid IP" << std::endl;
}

So it is actually very easy to achieve the same functionality in C++ as it is in rust.

Conclusion

Rust enums are an amazing rust feature but it not a feature that you cannot find in other languages. It might not be integrated in C++ but it is very performant and usable to use std::variant.