In this lesson, you will learn how to evolve your library’s error enum variants over time without breaking existing consumer code.
When you ship a library, your enum MyError acts as a public contract. If a consumer uses a match statement on that enum, adding a new variant is a breaking change because the match will no longer be exhaustive. Rust’s compiler will force the consumer to update their code, which is usually undesirable for minor library updates.
The #[non_exhaustive] Strategy
The primary tool to manage this evolution is the #[non_exhaustive] attribute. By marking your error enum with this attribute, you tell the compiler that users are not allowed to exhaustively match against it.
#[non_exhaustive]
pub enum MyError {
ConnectionFailed,
Timeout,
}
Because the enum is marked non_exhaustive, a consumer's match statement must include a "catch-all" arm (_ => ...). When you eventually add a new variant, such as AuthenticationFailed, the consumer’s code remains valid because their existing catch-all arm will handle the new case.
Implementing a Fallback
Since consumers can no longer rely on exhaustive pattern matching, they must provide a default path for unknown errors. You should design your API to handle this gracefully:
- Keep existing variants stable: Once a variant is part of your public API, try to keep its signature stable.
- Use the wildcard: Encourage users to use the
_pattern in their error handlers to accommodate future additions. - Documentation: Clearly communicate that your error types are subject to expansion, so consumers should always implement a default behavior for unhandled error scenarios.
If you don't use #[non_exhaustive], adding a single variant forces every user of your crate to update their code immediately upon upgrading. This creates "brittle" APIs that are difficult to maintain in a professional environment. By opting into this pattern, you effectively decouple the library's growth from the consumer's implementation details, allowing for smoother versioning and safer API evolution.
Key Takeaway: Use the #[non_exhaustive] attribute on public error enums to allow for future variants without breaking consumer code that uses pattern matching.