炸鱼薯条德里克
炸鱼薯条德里克

Reputation: 999

Rust can't use enum in const fn?

This code wouldn't compile, why Rust don't even allow this? It's just simple enum variation test, is there any way to workaround?

#![feature(const_fn)]

struct Test {}

#[derive(PartialEq, Eq, Copy, Clone)]
enum Vk {
    BLOCK,
    ITEM,
}

trait HasC {
    const d: Vk;
}

impl HasC for Test {
    const d: Vk = Vk::BLOCK;
}

const fn try_use_enum_in_const_fn<T: HasC>() -> bool {
    const a: Vk = Vk::BLOCK;
    T::d == a
}

fn main() {
    println!("{}", try_use_enum_in_const_fn::<Test>());
}

It gets error

error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
  --> src/main.rs:21:5
   |
21 |     T::d == a
   |     ^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0015`.

Playground

Upvotes: 1

Views: 1072

Answers (1)

SCappella
SCappella

Reputation: 10424

It's just simple enum variation test

Not exactly. You're using == which desugars to an application of the PartialEq method eq. Since const trait methods aren't possible yet (and the RFC for them hasn't even been accepted), you can't mark that method as const, so == is unusable in const functions.

Well ... almost unusable. There are a few exceptions for primitive types. Replace your T::d == a with 2 == 2 and everything compiles fine. The reason is that equality between certain primitive types (including integers and floats) doesn't use the PartialEq trait but is implemented more intrinsically. This intrinsic implementation can be used in const functions.


The most idiomatic solution might be to use a match or if let to check an enum variant, but unfortunately, match doesn't work in const functions yet either. If you're using nightly (and it appears you are) the feature flag const_if_match will enable it.

const fn try_use_enum_in_const_fn<T: HasC>() -> bool {
    if let Vk::BLOCK = T::d {
        true
    } else {
        false
    }
}

(playground)


It's somewhat of a hack, but on stable currently, you can reduce an enum variant check to equality between primitive integer types and take advantage of the compiler magic mentioned above. When none of the enum variants have data attached to them, they can be cast to primitive integer types. The first variant is by default 0, then 1 etc., though this can be overridden. (Though I'm sure you're aware that what you're doing with traits here isn't stable yet - so this won't work entirely on stable).

const fn try_use_enum_in_const_fn<T: HasC>() -> bool {
    const a: Vk = Vk::BLOCK;
    (T::d as isize) == (a as isize)
}

(playground)

Upvotes: 4

Related Questions