Allowing opt-out would undermine the non-exhaustive feature. The only thing you can do is add a catch-all match arm or convince the crate maintainers to remove the #[non_exhaustive] attribute.
There is a hack that will fail to link the binary when a non-exhaustive type is changed:
// Define an external function that will not exist
extern "C" {
fn _link_error();
}
fn f(kind: IntErrorKind) {
match kind {
IntErrorKind::Empty => todo!(),
IntErrorKind::InvalidDigit => todo!(),
IntErrorKind::PosOverflow => todo!(),
IntErrorKind::NegOverflow => todo!(),
IntErrorKind::Zero => todo!(),
// Fail to link when IntErrorKind adds new variants that are not covered
_ => unsafe { _link_error() },
}
}
It will compile only if all variants are covered. If any variants are not covered, you will get an obnoxious linker error. The error message will tell you which source lines are responsible, but it's always going to be hard to read. It can't be used in ![forbid(unsafe_code)] crates. It's not ideal for several reasons.
On the other hand, one could say that #[non_exhaustive] also undermines Rust's powerful exhaustive matching feature.
Having said that, non_exhaustive_omitted_patterns will be a good solution that strikes a balance between library developers who want to use #[non-exhaustive] and consumers who want exhaustive matching. I'd like to use that once it is stabilized.
I agree there is a natural tension between a consumer wanting to avoid runtime errors and a provider wanting to avoid breaking changes. And I would personally err on the side of not using the attribute. It hasn't ever been used it in any of my crates, and it's unlikely to ever appear in them.
My personal philosophy here is that breaking changes should be communicated effectively with SemVer and that breaking changes are broadly healthy for the ecosystem. So, #[non_exhasutive] not only undermines exhaustive matching, but also the responsibility of crate maintainers to control breaking changes in their public types.