But when there are multiple type parameters things start looking weird and makes downstream interaction cumbersome, especially since we don't have associated impl types yet. So a fn Result becomes this strange thing:
One way out was something along the lines of std::io::{Error,ErrorKind}, but I find that rather unpleasant compared to erasing the type using anyhow::Error.
Case in point: foca's Error enum. It used to have the wrapped errors (Encode/Decode/CustomBroadcast) with explicit types and using it was super cumbersome downstream...
Using anyhow::Error doesn't sound like a terrible idea: the type is super lean and there's now std::error::Error so it shouldn't become an integration challenge like failure was...
So, am I missing something or it is perfectly fine to have anyhow's Error in a public type?
There's certain advice I see going around that anyhow is a good choice for binaries, and thiserror is a good choice for libraries. Are you familiar with the latter?
It's not generally advisable to erase error types in libraries, as it's hard to predict what your clients might want to handle how.
Oh yeah, thiserror is pretty good for deriving the traits necessary to implement error, but it doesn't apply for my case: it's about propagating errors that I don't have full control of.
Perhaps the fact that Foca is no_std with optional std support and expects users to implement other traits to use it helps paint the picture better?
It sits in a space that's almost an application but acts like a library: users implement a Codec trait, for example, and Foca uses the codec, emitting foca::Error::Encode(CodecError).
Fair point and I agree. I'm thinking that in my case is not too bad to do so because there's a top level Error enum with a discriminant that wraps each error I don't control, so users should be able to downcast_ref::<RealErrorType>() with certainty. Is that still too annoying?
Sure thing. All thiserror does is implement other traits for me (From<SrcError> in the case of the attribute your pointed out). This isn't what I need because I don't know what the error is at that point.
std now has std::error::Error trait which would allow me to wrap any error I don't control, but then I would only be able to have a single discriminant. Something like:
I mean, if the error type is exposed as a public API, it's good to use your own custom error type rather anyhow::Error. This opnion is irrespective of the fact whether you internally use the error, either in another part of the same library, in another library or in a binary.
Is it? In fact, I'd expect anyhow's to perform/fit better than the two indirections.
In my case Box<dyn std::error::Error> isn't good enough because we don't have the trait in no_std, so I went for anyhow::Error. But it's in the same spirit as using your suggestion.