Hacker News new | ask | show | jobs
by iudqnolq 1339 days ago
Rust convinced me that having a section at the top of a function that early returns in error/exceptional situations works very well. I find myself missing this in the one functional language I've tried, Elixir, although I find Elixir worth it for other reasons.

I initially fell in love with the Rust convention of simply bubbling up errors throughout a function but I've gradually realized that leads to a poor API. [1]

I'm currently learning c++ for a personal project. I want to use a library with an API that's sufficiently templated it'll be easier to write a c++ shim than directly generating bindings. So far I'm at the stage where I'm constantly thinking Rust does things better. Hopefully later on I'll realize why the different tradeoffs c++ makes make sense. (One example would be how generic code is typechecked).

[1]: If you want to be able to simply propagate up errors with the `?` operator you need to implement automatic conversations from your dependencies' errors to your error type. But that means your API will have a single error type for every dependency error type - say MyError::Io(io::Error) - which is the wrong level of specificity. I now think you should either expose only a string message your user can just show their end user or an error that's much more specific so they can write code that intelligently responds to it. Something like MyError::ReadConfigFailed(io::Error)

1 comments

Regarding citation #1: I'm a Rust learner myself, so this is something that's rather salient for me. What do you think about the "boxing errors[1]" approach? This strikes me as one possible way you could bubble up dependency errors without being forced to deal with type conversions.

[1]: https://doc.rust-lang.org/rust-by-example/error/multiple_err...

When I said

> I now think you should either expose only a string message your user can just show their end user or an error that's much more specific so they can write code that intelligently responds to it.

a boxed trait object is the main way to implement an error that merely conveys a string message.

I see, thanks for the response! I guess you're looking for something more deterministic so you can more effectively pattern match against the error's type?
I think both are very good options. In a lot of cases it's not useful to deterministically match against the error type. The classic example of this is an application that's just going to pop up an error box, but this applies whenever your consumer can't fix the error even if you tell them specifics. In that case it's a significant waste of time to go beyond the boxed trait style (but anyhow or eyre add nice frills on top of a boxed trait).

Edit: Realized it might not have been clear that "boxed error" and "all you get are a string message" refer to the same thing. While you can try and convert a trait object to a specific type at runtime it's a crude and ugly approach that suggests that trait object should have been something else to start with. The main thing you do with a Box<dyn Error> is ask it for a Display message.