Enum with discriminant larger than 128 bits

Hi,
I recently stumbled upon a problem regarding implementing a bitflag like enum with more than 128 values.

In such a construct, each variant gets attributed a unique value whose binary representation has a single bit defined. The problem is that on stable, the maximum numbers of "bits" an enum variant can span accross is 64 (and 128 on nightly).

How do you implement something like that without overcomplicating the API (i.e., no more complex than what is exposed by bitflag or enumflag2 crates) ?

Rust enums aren't like C enums: you can't combine enum variants using | so you can't use them as bitmasks without listing all combinations. For instance in your code LongEnum::V0 | LongEnum::V1 is not a possible value.

1 Like

Yes, indeed, but my question was more focused on "how to get an enum with variants larger than 128 bits", then using this enum it is possible to implement a C-like enum by wrapping it in a custom API, like what's done by the bitflag and enumflag2 crates.

I have just looked at the bitflags crate and its implementation doesn't use the keyword enum at all. I think enumflags2 also doesn't use enum in its generated code.

Ok I just checked the result of expandanding the macros from bitflag and it is indeed true that they don't use enums at all. I guess the questions changes to "how to use integers larger than u128". In this case I guess I will have to use BigInts or arrays of smaller integer types. But then I loose the ability to Copy the "bitflag" type.

Now that I think about it, I could do something like this:

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct Flags {
    hi: u128,
    lo: u128,
}

impl Flags {
    const A: Flags = Flags::new(0, 0b01);
    const B: Flags = Flags::new(0, 0b10);
    const Z: Flags = Flags::new(0b01, 0);

    const fn new(hi: u128, lo: u128) -> Self {
        Flags { hi, lo }
    }
    
    const fn contains(&self, oth: Flags) -> bool {
        ((self.hi & oth.hi) == oth.hi) && ((self.lo & oth.lo) == oth.lo)
    }
}

impl BitOr<Flags> for Flags {
    type Output = Flags;
    
    fn bitor(self, oth: Flags) -> Self::Output {
        Flags {
            hi: self.hi | oth.hi,
            lo: self.lo | oth.lo,
        }
    }
}

fn main() {
    let v = Flags::A | Flags::Z;
    println!("{:?} {} {}", v, v.contains(Flags::A), && !v.contains(Flags::B));
}

And expand on it if needed.
Thank you @tczajka, I could not solve this beforehand because I kept thinking that I had to use an enum.

Indeed. Like 99% of questions on that forum it's XY problem: you want to bring code from a different language which makes no sense whatsoever into Rust.

It wouldn't work. Rust language doesn't support this use of enums and if you need macros you don't need to care about what actually keeps your bitmasks: enum, u32 or [u8; 128]. It would all abstracted behind your custom interface, anyway.

1 Like

99% of statistics are made up on the spot. Including the one in this reply of mine.


Seriously though, I think the number is wayyy lower than that. I see lots of great questions on this forum.

Even this thread’s original post, though containing false premises about wanting/needing to use enums, does IMO contain sufficiently much contextual information about what “X” is and is not limiting the question only to the “Y”, so I wouldn’t even fully consider this one an instance of “XY problem”.

3 Likes

Except it was an instance of XY problem:

Granted, it wasn't the really bad case: as you have noted the author explained about where the problem was coming from and it was easy to pick on that and give the right solution, but it was, most definitely the XY Problem in it's core.

True, but almost all of them fall into two categories:

  • Limitations of Rust (known and/or bugs in the compiler)
  • XY Problem (most often an attempt to write some other language in Rust)

Okay, I guess you are right and 99% is too much, first choice happens quite often, too, it just doesn't waste so much resources thus leaves less lasting impression.

But I still think it's way more than half, it would be less only if you include just the pathological cases where topicstarter fights all the people who try to help relentlessly by never revealing an actual goal.

Hmm… as far as my understanding went, I thought XY problem is specifically about question and answer situations; so the fact that OP before asking questions was confused or hindered by their approach of trying Y to solve X but failing at Y, too, should be irrelevant for classifying this thread as an instance of XY problem.

That being said, this context of course means that there was the potential for a more severe XY problem case if the question had been asked differently and/or the answers hadn’t picked up on the false premise so quickly.

That’s also a question of definition, I suppose. Going through the listing you liked to

The XY problem is asking about your attempted solution rather than your actual problem. This leads to enormous amounts of wasted time and energy, both on the part of people asking for help, and on the part of those providing help.

  • User wants to do X.
  • User doesn't know how to do X, but thinks they can fumble their way to a solution if they can just manage to do Y.
  • User doesn't know how to do Y either.
  • User asks for help with Y.
  • Others try to help user with Y, but are confused because Y seems like a strange problem to want to solve.
  • After much interaction and wasted time, it finally becomes clear that the user really wants help with X, and that Y wasn't even a suitable solution for X.

The problem occurs when people get stuck on what they believe is the solution and are unable step back and explain the issue in full.

There are some points completely not applying here, especially towards the end about “others try to help […] but are confused” or “much interaction and wasted time”. It’s not for me to decide how essential it is that all points are met, but in my personal opinion/interpretation, a case where there was no confusion and no time wasted shouldn’t really count at all.

If the original post would’ve just been asking about “how can I have an enum type with a discriminant larger than 128 bits?” and the answers would’ve started to go into “confused” territory, e.g. addressing “why would you need more than 128 bits, since 2128 is a lot of enum variants”, then it could have gone full XY problem.


Hmm fair enough, I guess it shouldn’t be taken too narrow, too…

On the other hand, you can’t criticize all posts asking “how can I do Y to achieve X” for not adding a “I tried Y here, but I cannot be certain that it’s a reasonable approach in the first place” disclaimer every time, because as the person asking the question, it can be quite hard to know in what cases you made an incorrect assumption / conclusion, and adding such disclaimers everywhere – even for the most basic and unquestionable conclusions you’ve drawn as the author of a post – can probably make the post look quite weird, too.

As long as people asking do include enough context to their questions, IMO they may as well write out all the false premises they want; can even make it easier to answer if people clearly state their (possibly incorrect) assumptions… “Hey, this premise of yours is actually wrong for such-and-such reason.” – “Ah! I never realized, thanks, that alone already helps me out a lot!”. I think this thread might even be an instance of that.

Fair enough. I think at this point we are mostly down to the precise definition of XY problem: whether the fact that topicstarter tried to solve the wrong problem in the first place is more important or whether topicstarter refused to clarify why s/he thinks it's good idea to discuss that problem.

I would say that huge percent of threads in URLO is because people start discussing wrong problems but that usually, beause XY Problem is so common on URLO and mistakes are so well-known too, it doesn't become a centithread where topicstarter insists that X problem must be solved and doesn't ever reveal the actual Y problem.

Real true pathological cases where Y problem doesn't even exist are actually pretty rare.

1 Like

Yes, certainly. I just wanted to make sure that – in case we’re applying a very broad definition of the term – we should make sure that it doesn’t get interpreted as (too strong a) criticism of those “XY problem” threads; because at least I’ve seen the term commonly (at least implicitly) criticize the person asking a question, as further evidenced e.g. by the fact that the website you linked also only proposes “solutions” that ask for change of behavior in the person asking the question

What to do about it?

  1. Always include information about a broader picture along with any attempted solution.
  2. If someone asks for more information, do provide details.
  3. If there are other solutions you've already ruled out, share why you've ruled them out. This gives more information about your requirements.

Remember that if your diagnostic theories were accurate, you wouldn't be asking for help right?

whereas if you merely drew a false “I want to solve X, try Y but get stuck on Y” conclusion for yourself then asking for help about it is the perfect approach.


I also feel this forum is helped here by the forum nature of writing longer and more detailed posts. As soon as the question is long and detailed enough, a truly bad “XY problem” situation is already almost impossible; probably also the reason why the website you linked shows two XY problem examples that are happening in a chat. Other occurrences of XY problem terminology I’ve heard/read about – and that’s a completely different area – would be in the are of tech support, where the format could be chat or maybe talking on a phone, or E-Mails, or whatever, but the problem can be worse by the fact that the person asking for help is often generally much less technically versed; pair that with a potential attitude of “I don’t want to bother with this problem anymore”, and you might get the sufficiently concise and confidently presented wrong “I need to solve Y” problem statements that go undetected for a while… except that tech support people would probably be well versed in avoiding such problems, too.

Another aspect is that people asking questions in this forum often want to learn about something, or about how to do something, rather than needing help on just doing/solving on particular thing once.

Yet another aspect I can imagine, and I cannot confirm this due to lack of experience in other forums, but I would assume that the strict nature of the Rust compiler makes it a lot more unlikely for people to go all that deep into wrong partial solutions before asking for help. Whereas in less strictly typed languages, I can imagine people going so deep into their assumed-to-be-reasonable unreasonably approaches that – by the time they run into problems and ask for help – they might have forgotten themself what exactly their original problem that got them do go that way even was, and what was not part of that original problem.

Of course in cases where the Rust compile doesn’t help you, we still get such questions even in Rust. Off the top of my head, I can think of

  • people completely defeating the borrow checked using bad unsafe code… then later asking about some specific detail (“is this safe”?) or about a specific outcome of their UB (“why is this behaving weirdly?”) unaware of the (large) mess of broken unsound code they’re working with
  • people not really understanding async using a lot of blocking code in their futures and then asking about how to solve a particular weird behavior coming up
1 Like

You can also do things like struct Flags([u64; 4]);, which you could adapt into a const generic later, should that be useful.

Then you have self.0[i / 64] |= 1 << (i % 64); to set a flag, for example.

1 Like