Enumerated types (enum)

In Java, an enum is a specialized type of class that has limited functionality. An enum holds a small number of possible permissible values of a type.

Here's an example of an enum in Java:

enum PrimaryColor {
    RED,
    GREEN,
    BLUE
}

Rust has an identical syntax for declaring the same enum:

enum PrimaryColor {
    Red,
    Green,
    Blue
}

Being specialized classes in Java, enums can have member fields and methods. Here's an example for illustration purposes:

enum RainbowColor {

    RED(1),
    ORANGE(2),
    YELLOW(3),
    GREEN(4),
    BLUE(5),
    INDIGO(6),
    VIOLET(7); // the semi colon at the end of list required for enums with parameters

    private final int number;
    private final String name;

    public int getNumber() {
        return number;
    }

    public String getName() {
        return name;
    }

    RainbowColor(int number) {
        this.number = number;
        this.name = switch (number) {
            case 1 -> "RED";
            case 2 -> "ORANGE";
            case 3 -> "YELLOW";
            case 4 -> "GREEN";
            case 5 -> "BLUE";
            case 6 -> "INDIGO";
            case 7 -> "VIOLET";
            default -> throw new RuntimeException("Illegal: " + number);
        };
    }
}

Here's how we could exercise the RainbowColor enum:

public class RainbowColorTest {

    public static void main(String[] args) {
        RainbowColor color = RainbowColor.BLUE;

        String name = color.getName();

        System.out.println(name); // prints: BLUE
    }
}

A slightly similar (not a 1:1 mapping) version of the RainbowColor enum in Rust is shown below:

#[derive(Debug)] // enables formatting in "{:?}"
enum RainbowColor {
    Red = 1,
    Orange = 2,
    Yellow = 3,
    Green = 4,
    Blue = 5,
    Indigo = 6,
    Violet = 7,
}

impl RainbowColor {
    fn new(number: i32) -> Result<RainbowColor, Box<dyn std::error::Error>> {
        use RainbowColor::*;
        match number {
            1 => Ok(Red),
            2 => Ok(Orange),
            3 => Ok(Yellow),
            4 => Ok(Green),
            5 => Ok(Blue),
            6 => Ok(Indigo),
            7 => Ok(Violet),
            _ => return Err(format!("Illegal: {}", number).into())
        }
    }
}

The new() function returns a RainbowColor in a Result indicating success (Ok) if number is valid. Otherwise it panics:

let color = RainbowColor::new(5);
println!("{color:?}"); // prints: Ok(Blue)

let color = RainbowColor::new(10);
println!("{color:?}"); // prints: Err("Illegal: 10")

An enum type in Rust can also serve as a way to design (discriminated) union types, which allow different variants to hold data specific to each variant. For example:

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

let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));

Enums in Rust are most similar to algebraic data types in functional languages, such as OCaml and Haskell.