3 ms·
How does that compose? If you call somebody else's function, do you just create a superset of all possible errors they can return? What if it is a library that
by agent327 1y ago
How does that compose? If you call somebody else's function, do you just create a superset of all possible errors they can return? What if it is a library that doesn't really specify what errors an individual function can return, but just has errors for the whole library?
- j-pb 1y agoYou return an error specific to that function. If it internally has a `InnerFuncErr::WriteFailed` error, you might handle it, and then you don't have to pass it back at all, or you might wrap it in an `OuterFuncErr::BadIo(inner_err)`or throw it away and make `BadIo` parameterless, if you feel that the caller won't care anyways. Errors are not Exceptions, you don't fling them across half of your codebase until they crash the process, you try to diligently handle them, and do what makes sense. So you don't really care about the union.
- slau 1y agoThere's a bunch of different situations that can be discussed, and it's hard to generalise. However: Your function typically has a specific intent when it tries to call another function. Say that you have a poorly designed function that reads from a file, parses the data, opens a DB connection and stores the data. Should I really expect an end-user to understand an error generated by diesel/postgres/wtfdb? No, most likely I want to instruct them to generate debug logs and report an issue/contact support. This is most likely the best user experience for an application. In this case, each "action" of the function would "hide" the underlying error––it might provide information about what failed (file not found, DB rejected credentials, what part of the file couldn't be parsed, etc), but the user doesn't care (and shouldn't!) about Rust type of diesel error was generated. To answer your question specifically, I might go without something like this: #[derive(Debug, Error, Clone)] pub enum MyFunctionError { #[error("unable to read data from file: {0}")] ReadData(String), #[error("failed to parse data: {0}")] Parse(String), #[error("database refused our connection: {0} (host: {1}, username: {2})")] DatabaseConnection(String, String, String), #[error("failed to write rows: {0}")] WriteData(String), } Obviously this is a contrived example. I wouldn't use `#[from]` and just use `.map_err` to give internal meaning to error provenances. `DatabaseConnection` and `WriteData` might have come from the same underlying WTFDbError, but I can give it more meaning by annotating it. When building a library, however, yes, I most likely do want to use `#[from] io::Error` syntax and let the calling library figure out what to do (which might very well giving the user a userful error message and dumping an error log).