<Soni> do you know how ppl use checked exceptions?
<Soni> (are you familiar with how ppl actually use them?)
<Soni> so, there are 2 main ways ppl usually use them: the try { } catch (IOException e) { throw new RuntimeException(e); }, and the throws Throwable
<Soni> in Rust, these are also known as unwrap()-everywhere and docs.rs/anyhow
"Internal": This should never happen, don't complicate the API and just die: unwrap() / RuntimeException
"Library": This is something the caller should care about and handle specifically: enum Error / throws SomeError
"Application": This can happen but there's not terribly much the caller can do to fix it so it's not worth specifically handling, just print and exit or whatever makes sense for the what the caller is doing: anyhow / throws Throwable
I will say that really good "library" errors are by far the hardest; rust does make them nicer than my experience in other languages, but it's still relative. If you're not actually writing a library though, it's fine, just toss in anyhow until you actually need something more!
Errors in Rust have parallels because there's underlying reasons for those parallels, that's the point of my previous reply? Technically there's not much in common at all, and they got there from very different histories.
checked exceptions are functionally the same as Result. the developer experience is the exact same (tho the syntax is a little more verbose for checked exceptions vs Result).
propagating them? as easy as adding throws. handling them? as easy as catching them. unwrapping them? convert them into unchecked exceptions. and, like Result, the compiler forces you to deal with it.
however:
there's no real-world programming language where you define the exceptions in your API contract, and then those are the only exceptions you care about in the body of your function.
rust forces you to think about all the errors from your libraries. python forces you to... not think about them? (it doesn't have checked exceptions) java has a mix of both, due to mixing of checked and unchecked exceptions.
indeed, if you have an impl Read for YourType, you actually need to turn everything into io::Error! (or unwrap it.) isn't that a little ridiculous? (and indeed this is also a problem with checked exceptions. it's the same problem!)
instead, what if there was a way to say "this is the API contract, these are the only things the function should be responsible for handling and/or propagating". everything else passes through as-is because it's irrelevant to the API contract anyway, but you, as the implementer (not the caller) has to handle API-relevant exceptions from any libraries/etc you use in your implementation. this avoids the problem of putting errors inside errors (unless you like your 1GB error structs for some reason), this reduces cognitive overhead, and this still sidesteps checked exceptions.
in fact... these "this is the API contract" blocks don't even need to be in the function signature, since they're purely internal to the function. alas we don't think anything similar to this is possible in rust today, so we can't give an example of what it'd look like, but trust us when we say it's really all the benefits of unchecked exceptions with none of the drawbacks! and, since this unlocks the true power of exceptions, you might even call it exception-oriented programming.
This is only true for some instances of functions that return Result. You also have functions like Rc::try_unwrap that aren’t really exception-like at all— Instead, they use the Err variant to return the input back to you if the operation couldn’t be completed.
Could you elaborate on this? Are you saying that error handling should be implemented more or less strict depending on the type of software?
Wouldn't you in an API also desire nice error handling with different error types like io, parsing-errors, and even basic HTTP status codes like 409 for duplicates? So the user of the API knows what went wrong?
Results are different in that they force you to handle the error when they occur, you cannot handle all errors once. A small but (positively IMHO) important difference.
Overall I think they are similar but somehow Rust developers seem to better handle Results than Java developers handle checked exceptions. Don't know the reason though.
That's the major drawback exception error handling has, and would be a major step back for Rust.
Having errors as part of the function signature is not to increase the cognitive load. On the contrary, it's precisely because there's nothing hidden from us as developers that we can better reason about the code (let alone making it easier to perform static analysis) and its intended behaviour.
If there's anything to change (improve) about Rust's error handling, I think it would be about panics.
This seems contradictory? If the API contract is that you need to handle FileNotFound, that by definition is not purely internal.
You can model this in Rust with something like anyhow::Result<my::Result>, but I would appreciate an ergonomic Result::try_map_err or something. Having a match with a fallback result @ Err(_) => result? or Err(error) => return Err(error.into()) isn't so bad though...
More that the type of error contract depends on what the called code knows about how the calling code will use it - like any API really, but errors tend to fall into those three buckets, and most often "library" errors lines up with them being part of a public library crate, which is unlikely to know if the caller will want to handle different types, hence the naming.
if the API contract is that it raises FileNotFound when the desired file is not found, then it shouldn't raise FileNotFound when the config file is not found.
the latter would, in fact, be a hard-to-trace bug. because it would be indistinguishable from the desired file not being found.
Did... what? I'm saying I don't think I understand the feature or functionality you're asking for here;
What it sounds like you're asking for is something like negative throws clause, where you declare which exceptions you won't throw. But you could then just ... catch them? Or in Rust, match on them?
This probably just means I simply don't understand what you're actually asking for; perhaps a pseudocode example might help?
I may also misunderstand. But I think the idea is to declare that certain errors in the fn body should be automatically handled by converting them to some sort of implicit runtime error, that can be specially caught at any level higher in the call stack. Any example would be the std:io::Error returned from a std::io::Cursor, since these should never occur.
This is a nice idea, and something I’ve thought about a little bit before. I think with a correctly-implemented effects system it would be possible to do this. I even wrote a whole blog post about it: