Why do many Error types in lib restrict external instantiation

today, I find some Error type of other lib can not be instance, e.g.:

pub struct ParseIntError {
    pub(super) kind: IntErrorKind,

source by rust std

I should not to be let a = ParseIntError {...} with instancing it. I think there are some benefits to this, but I don't know why to do it.


Errors are private, library-specific, and you are supposed to convert them to your domain errors when handling them. The API is telling you this by not letting you create instances of them.


This doesn't seem to affect type conversion

The standard library in particular tends to default to exposing at little surface area as possible, because once something is on stable, the exposed parts must be supported forever, and they might be forced to create some new types and deprecate the old instead of iterating internal details.

See for example the conversations and process around stabilizing ParseIntError::kind. The process started in 2015 before #[non_exhaustive] existed; if it had been stabilized even in its current form then,[1] it wouldn't have been possible to add the new Zero variant when FromStr was implemented for non-zero integer types.

  1. much less if it had been made fully public ↩ī¸Ž


Errors in Rust are basically human-meaningful manual implementations of backtraces. The best practice consists of special-purpose error types for each logical unit of code (which can be as small as single functions, or at worst as large as a library). If it's possible to instantiate foreign errors in your own code in entirely unrelated context, they can't serve the purpose of backtraces, can they? Getting some error type wouldn't give you any information on where the error originates from.

This is marred by the fact that you can still directly propagate foreign error types from your functions, without converting them into your domain-specific errors. Ideally you should do it only when your own function doesn't have any more meaningful reason for those errors.