I don't really understand the use case in the initial question: surely, if one is adding a new feature, exerting that feature is going to go through new API functions that will be able to return new, different Error
enums without affecting existing code?
Anyway, assuming that you do have to add a new error variant to an existing error enum, Clients can "opt-out" of a breaking change on an enum by always having a default arm branch:
#[allow(unreachable_patterns)]
match error {
Error::A => { /* ... */ }
Error::B => { /* ... */ }
Error::C => { /* ... */ }
_ => { /* special error-handling for unknown errors, may be unreachable if all variants are currently handled */ }
}
As a client of a library, I much prefer having to manually use this way of "opting-out" when the particular error variant doesn't matter to me, rather than the library forcing that behavior on me by default.
Being able to exhaustively match on error variants of an enum is a great advantage of rust when you need precise control over what to do with all kinds of errors that can happen: if there is a newly added error variant to an error enum, then every place where you matched on this enum will automatically not compile anymore, so that you can precisely choose what to do of the new variants.
If I understand correctly, the #[non_exhaustive]
attribute negates that advantage.
Yes, it means that adding a new error variant requires bumping the major version of the library, but I think this is to be expected: you are adding a new way for your library to fail, I don't believe this behavior should be "silently" pushed downstream.
Now, that doesn't mean that the #[non_exhaustive]
attribute cannot have its uses, just that I do not think that it is fit for error handling in a library, in general...