Stability guarantees of coercing an enum into i32


#1

I’ve been writing a date-time library. It has a Month enum to avoid the problems associated with months as numbers (is “Month 1” January or February? What happens if you pass in a month out of range?), and I deliberately didn’t include default values for the enum variants so users of the library wouldn’t try to use them as numbers. In other words, I had this enum:

enum Month { January, February, March, ... }

Then, a user noticed that these could be converted to numbers anyway, and proposed this change:

enum Month { January = 1, February = 2, March = 3, ... }

I didn’t realise that enums could automatically be converted to numbers, even if you didn’t assign them any values! So I’m asking: does adding values to enum variants count as a breaking change? From my perspective, I didn’t give them any values on purpose, so wouldn’t have expected people to use this feature. But on the other hand, the compiler allows this, and changing it is going to break people’s code. What do you think?


#2

Well a bit inspired by the RFC: Policy on semver and API evolution… it is a breaking change.

However, is it a major change, minor change?

Crate versioning is about communication. You can only break a promise you have actually made, implicitly or explicitly. So it is up to interpretation. In libstd I think this would be a major breaking change — to do otherwise it would probably do something to avoid having users cast the enum to an integer in the first place.

For clarity, in the rest of the RFC, we will use the following terms:

  • Major change: a change that requires a major semver bump
  • Minor change: a change that requires only a minor semver bump
  • Breaking change: a change that, strictly speaking, can cause downstream
    code to fail to compile.

What we will see is that in Rust today, almost any change is technically a
breaking change. For example, given the way that globs currently work, adding
any public item to a library can break its clients (more on that later). But
not all breaking changes are equal.

So, this RFC proposes that all major changes are breaking, but not all breaking
changes are major.


#3

Oh that’s cool - I heard of that RFC but didn’t know it was so detailed! It doesn’t mention my particular use case, but the impression I got was that there’s no way to tell the difference between missing out the variant values on purpose (because they don’t make sense for the enum), just not implementing yet, and missing them out because the default order is correct, so I’d have to treat it as a major change because it might break code with no warning.

Fortunately, my library was at version 0.3, so I’ve technically made no stability promises anyway.