4 ms·
I don't know how this unexpected vs expected errors philosophy gets propagated, but to me it always looked suspicious. Take array bounds for example: what if yo
by spion 10y ago
I don't know how this unexpected vs expected errors philosophy gets propagated, but to me it always looked suspicious. Take array bounds for example: what if you have an API that lets users send a list of image transformations, and the user requests a face-detect crop, followed by a fixed crop with a given x, y, w, h.
Clearly your code can get out of (2D) array bounds with the fixed crop (if the image is such that the face-detect crop ends up small enough). Suddenly the thing that was "unexpected" at the array level becomes very much expected at the higher API level.
So the API provider can't decide whether an error is expected or not. Only the API consumer can do that. Applying this further, a mid-level consumer cannot (always) make the decision either. Which is why exceptions work the way they do: bubble until a consumer makes a decision, otherwise consider the whole error unexpected. Mid-level consumers should use finally (or even better, defer!) to clean up any potentially bad state.
I think Swift got this right. What you care about isn't what exactly was thrown (checked, typed exceptions) but the critical thing is whether a call throws or not. This informs us whether to use finally/defer to clean up. The rest of error handling is easy: we handle errors we expect, we fix errors we forgot to expect, but either way we don't crash for clean up because finally/defer/destructors take care of that.
- nickm12 10y agoI don't understand your example. The API provider has to determine the contract and decide whether out-of-bounds crops are supported or not. If they are supported, then an out-of-bounds panic is a bug in the API. If they are not supported, then an out-of-bounds panic is a bug in the caller. In either case, the exception is unexpected and due to someone not following the contract.
- blaisio 10y agoYour example doesn't make sense. Keep in mind that an out of bounds array access can cause a lot of problems; in the best case, it would cause a segfault, and in the worst it would allow for code injection. So there isn't really a case where accessing an array out of bounds makes sense. That's where the idea of "expected vs unexpected errors" comes from. Another way to think about it is that some errors can be handled and some can't. For example, most of the time you can't "fix" a segfault, so it makes sense for that to just crash the program.
- oconnor663 10y agoIn cases where the caller might want to handle an error, Rust almost always uses an Option/Result. For vectors, the `get` method lets you try to get an element out without panicking if your index is bad. (Or of course you can just check the index manually. This is guaranteed to be threadsafe, which is nice.) The try! macro is the standard way to say "I don't want to handle errors here -- you take care of it."
- Manishearth 10y agoThis is why almost every panicky API has a non-panicky variant. Non-panicky APIs can be used in a panicky way via unwrap() or expect(). There are very few panicky APIs, reserved for things where 99% of the time you want it to panic and it would be annoying to have to handle errors all the time. Array indexing is one of these cases, where it would be super annoying to handle errors on each indexing operation. But if you're in a situation where you expect an index to fail, just use `get()` which returns an Option instead. APIs do not make the decision for you. At most they decide if the panicky API is easier to use than the monadic error one. Which is where the fuzzy notion of "unexpected" and "expected" errors at the API level comes up, it's good enough to be able to determine what the default should be. Callers still have the power to do something else.
- dikaiosune 10y agoI would kill to have a monadic slicing API for Vec<T>, though. It seems like the one significant exception to the "use whichever works better for you" argument. Of course you can bounds check yourself first and return Err if there's a problem, but there's also no get_unchecked() for slices so you'd pay for the checks twice.
- Sean1708 10y ago> but there's also no get_unchecked() for slices so you'd pay for the checks twice. Yeah there is[1], unless I'm being really stupid... [1]: https://doc.rust-lang.org/std/primitive.slice.html#method.get_unchecked https://doc.rust-lang.org/std/primitive.slice.html#method.ge...
- dikaiosune 10y agoRather, there's no get_unchecked to get a slice from a Vec. Unlike index, get_unchecked can't take a range as input.
- Sean1708 10y agoAh gotcha, sounds like it could be a good first RFC for someone.