Search code examples
rusttypesenumsstrong-typing

Raising compile-time errors in order to constraint possible permutations of a struct in Rust


Imagine a struct...

struct Pair {
    letter: Letter,
    number: Number,
}

enum Letter {A, B, C}
enum Number {One, Two, Three}

The total number of combinations in this struct would be 3 x 3 = 9, however, I want to make only some of them possible. Say, I allow all combinations to exist, but {C, Three} cannot exist, for whatever reason.

How can the type system in Rust help me achieve this such that if I have a constructor for Pair...

impl Pair {
    fn new(letter : Letter, number : Number) -> Pair {
        ...
    }
}

and then later in my main function I do... Pair::new(Letter::C, Number::Three); the compiler raises a compile-time error instead of panicking during runtime?


Solution

  • One possibility is to use const eval to make sure invalid combinations don't compile:

    impl Pair {
        const fn new(letter: Letter, number: Number) -> Pair {
            assert!(!matches!((letter, number), (Letter::C, Number::Three)));
    
            ...
        }
    }
    

    But marking the function as const does not guarantee it will be evaluated at compile time, only makes that possible. To guarantee that, you can use a macro:

    macro_rules! new_Pair {
        ($letter:expr, $number:expr $(,)?) => {{
            const V: Pair = Pair::new($letter, $number);
            V
        }};
    }