Imagine that you pull in crate foo
as a dependency. It has a foo::Error
for its own errors, so you set up a From
conversion to do implicit conversion from foo::Error
to your own library/application error.
At some point you call foo::Foo::do_something()
, and the implicit conversion doesn't work. It turns out that foo
uses bar
as a dependency, and Foo::do_something()
is a very thin wrapper around some function in bar
, and for whatever reason foo
's developer chose to have the wrapper be so thin that it returns bar::Error
.
I have encountered this in the wild, so it's not hypothetical -- but I'd say its rare (as far as the crates/functions I have used). Personally I tend to wrap other crate's Error
's in my own errors, because I don't want end users to have to pull in separate crates just to get access to Error
types.
There is a sort of a middle-ground, I guess: Re-export the dependencies, so the developer doesn't need to explicitly import them in their own Cargo.toml
.
Where on the transparent-to-opaque scale do we want sub-dependencies to, generally speaking, be in the ecosystem?
Back-story: I'm in a situation where I set out to remove some in-crate objects and use dependencies instead. I am pleased with the result, except for one particular place where I expose a foreign Error
. I can work around this by adding an error-conversion wrapper -- but this is the sort of thing I set out to clean up. So it got me questioning why I put so much effort into hiding sub-dependencies?
I have always treated dependencies opaquely, even back in the C/C++ days -- it never really occurred to me to not do it in Rust. But apart from "following tradition" and "being idiomatic", are there technical reasons why crates tend to be opaque? What manner of can-of-worms would one be opening up if one would start to lean on the more transparent side of things?