Thanks, it means a lot!
In my proposal, the public API of the crate is clearly exposed. Modifying the variants returned by failable()
would result in a compile error in export_enum()
. It it therefore easy for the programmer to know that such change would requires a major version bump if it was deemed to propagate through the public API. Such changes would be immediately obvious (you would get a compilation error), so it would be easy to fix it since you would know what was just changed.
// the concrete variants are part of the private API of the crate
fn failable() -> Result<_, enum impl Error>;
fn call_failable() -> Result<_, enum impl Error> {
// ...
failable()
}
// public function visible outside of the crate
// its only purpose it to make the concrete variant part of the public API of the crate
pub fn export_enum() -> Result<_, enum(io::Error, sql::Error, http::Error) {
call_failable()
}
// downstream consumer of the above crate
fn usage() {
// We can easily know what we can match on since the types are
// part of the API (and thus documented)
match export_enum() {
// exaustive match, no `Err(_: Error)` case
Err(io: io::Error) => ...,
Err(sql: sql::Error) => ...,
Err(http: http::Error) => ...,
Ok(ok) => ...,
}
}
But if we remove the crate boundary restriction, the following snippet becomes valid. As such, modifying the concrete variants returned by failable()
would be a source-breaking change for downstream user, even if it's not obvious at all.
// the concrete variants are part of the private API of the crate
fn failable() -> Result<_, enum impl Error>;
fn call_failable() -> Result<_, enum impl Error> {
// ...
failable()
}
// public function visible outside of the crate
// the variants of the anonymous enums (io::Error, sql::Error, http::Error)
// are part of the public API of the crate even if it's not obvious at all
pub fn export_enum() -> Result<_, enum impl Error) {
call_failable()
}
// downstream consumer of the above crate
fn usage() {
// We must "guess" the types that we can match on, since it's not part
// of the public API, and we may not be familiar with the internals of
// the above crate (at least since the match is exhaustive, we would get
// a compilation error in case of mismatch)
match export_enum() {
Err(io: io::Error) => ...,
Err(sql: sql::Error) => ...,
Err(http: http::Error) => ...,
Ok(ok) => ...,
}
}