Comment

Comment by demurgos

At scale, `#[non_exhaustive]` is the right default for public enums that are part of your API model when working on a distributed system. You should still be able to opt into exhaustive enums when needed but in practice it's way rarer.

I'm working at a company with a very large Rust codebase with thousands of endpoints. We use multiple repos and each service publishes its API as crates. A few years ago those were manually written and teams used exhaustive enums frequently in their API. It did not prevent runtime issues as older clients could receive responses with new variants from newer servers. It also made versioning painful because semver would dictate a major bump, but Rust's nominal typing means that types from different major versions get different identities, making it very inconvenient to integrate with these types (think shared helper functions, extension traits, ...). This caused people to release new variants in feature bumps and wreck havoc in the dependency tree by breaking compilation of intermediate libs.

Since then we switched to code generation from an IDL for the API model. I was leading this project and decided to produce non_exhaustive enums for the Rust code: it was 2 years ago and the situation drastically improved.

We have a CI step running lints with `non_exhaustive_omitted_patterns` mentionned in an other comment. That's the right approach for such a public API. The big difference is that is that warnings are local to the crate you're working on, so they're more actionable than errors.

This was all in the context of modeling the API of a distributed system. I still tend to use non_exhaustive more often now even for regular helper libraries. It always requires some thought as you need to plan for the forward compat of your code.

parent demurgos 9 days ago

Discussion

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton