Idiomatic way to do flags-like groups

Coming from other languages like C++, having flags like this is pretty standard:

enum PizzaToppings {
   Pepperoni = 1 << 0,
   Tomatoes = 1 << 1,
   Pineapple = 1 << 2,
   ...
};

Then you can just smash them all into a single field and functions like:

const auto toppings = GetTopingsForPizza();
if (toppings & Pineapple) {
   printf("Hawaiian pizza!");
}

I know that I can do this in Rust right now. I can add a bunch of traits to an enum to make it work with all of these bitwise operations, conversions, and all that stuff. However, I am getting the feeling that in Rust, this may not be the most idiomatic way of going about this. There is a bit of boilerplate required... or I have to bring in external crates to do this, which further makes me think that this is not the right way of doing it, or it would have just been trivial.

Using a HashSet would also work, but involves allocation and is obviously pretty wasteful... Is there any consensus on how to do flagging in Rust without shoehorning in practices from other languages?

I think you want the bitflags crate. I've not used it, so I can't really give an example how to use it, but I do think it does what you want

Edit:

my bad, i missed this.

It is right way to doing it in Rust to bring some external crates. The Rust is a modern language distributed with the first party dependency manager and the package registry from its very 1.0 release.

1 Like

Thanks -- it does seem strange that such a low-level concept needs third party support to be convenient to do. This was making me wonder if the language is trying to steer me into not using flags at all by not providing a built-in facility for them. I'm not even sure if enums with explicitly set constants are fully idiomatic at this point.

You could make a newtype with associated consts and a BitAnd and BitOr impls, i.e.

struct Flags(u32);

impl Flags {
    const PEPPERONI: Self = Self(1);
    const TOMATOES: Self = Self(1 << 1);
    const PINEAPPLE: Self = Self(1 << 2);
}

impl std::ops::BitAnd for Flags {
    type Output = Self
    fn and(self, other: Self) -> Self {
        Self(self.0 & other.0)
    }
}

impl std::ops::BitOr for Flags {
    type Output = Self
    fn or(self, other: Self) -> Self {
        Self(self.0 | other.0)
    }
}

But I still would recommend bitflags (it creates all this boilerplate for you), see @Hyeonu's response

1 Like

A lot of stuff in Rust is handled solely by external crates (async, thread pools, http, etc.), doesn't mean it's not supported or suggesting you don't use it :slight_smile:

That makes sense, thanks! I feel much better about doing this now. I guess I just really wanted to avoid the JavaScript-style dependency craziness where something stupid like left pad gets deleted because author has a meltdown and half the internet goes down :slight_smile:

Luckily, crates on crates.io can't be deleted. They can only be yanked, which means nothing new can depend on them, but anything that already used them can continue to

By the way, bitflags is not even exactly third-party - one of its owners is rust-lang-nursery, so it can be thought of as "officially blessed".

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.