Course contentsShow
← Rust
Lesson 897 of 1,9107. Error Handling PatternsFree lesson

Versioning Error Variants

Manage the evolution of error variants in library APIs without breaking consumers.

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:

  1. Keep existing variants stable: Once a variant is part of your public API, try to keep its signature stable.
  2. Use the wildcard: Encourage users to use the _ pattern in their error handlers to accommodate future additions.
  3. 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.