3 ms·
The `?` operator is just syntax sugar for: let value = match attempt_something() { Ok(value) => value, Err(error) => return SomeErr::Kind(error), } T
by mmstick 10y ago
The `?` operator is just syntax sugar for:
let value = match attempt_something() {
Ok(value) => value,
Err(error) => return SomeErr::Kind(error),
}
That way you only need to type
let value = do_something().map_err(SomeErr::Kind)?;
It was called for by the community because error checking is something that can happen multiple times within the same function. Simply passing the error up the chain and handling all your errors in a central location is highly convenient, especially when matching requires that you handle all cases.
Additionally, in no way is C++ more efficient at error handling that Rust. Rust is able to perform this with zero allocation costs.
- akiselev 10y agoThe best part is that you can use Into/From conversion traits and use a different error type all through the call chain. A bunch of functions returning error type A and calling functions with error types B, C, D, and E can all just pass through the errors even though they return a different type. You implement the conversion trait once for each error and then it just works for all functions with no other boilerplate.
- softc 10y agoWith this method, do you have to implement the From trait for every pair of errors that can be propagated up the stack?
- softc 10y agoI understand exactly what "?" does, I just don't want to have to type it every time I call a function that can fail. Additionally I don't want to have to distinguish between which functions fail and which don't, I'd rather assume all functions fail and make sure my code is robust enough to handle failure at any point. This is my preference. C++ is more efficient at error handling than Rust. In the common case of no error happening, Rust must check a return code, but C++ doesn't have to because of how exceptions are implemented in the Itanium ABI "zero-cost exceptions".
- mmstick 10y agoThere is no cost associated with Result/Option when no error is returned. The compiler is smart enough to understand when failure is not an option. Only when an error occurs does Rust act on it. Additionally, it's nonsense to state that you don't want to handle errors when they occur, but want to write software that can handle failures at any point. Software libraries should handle their errors properly in the event that an error is possible in a function so that users of that library can handle them. Binary applications should handle errors when they occur versus randomly throwing them into the air and hoping for the best. The `?` operator is highly convenient in handling errors wherever they may occur whereby the error needs to be handled. You do not have to use the `?` operator if have other means of handling the error. In example, maybe you want to use a default value in the event of a failure: let value = attempt_value().unwrap_or(default); Or maybe you want to keep trying until it succeeds: let value = loop { if let Some(value) = attempt_value() { break value } thread:sleep(Duration::from_millis(1)); }; It's up to you how you handle your errors. The Option and Result types have a lot of methods for handling, such as and_then and or.
- softc 10y agoHmm no. Rust always issues a compare instruction after a function call to verify if it was successful or not. In a function that can fail depending on runtime conditions, the compiler is unable to elide such a check. Also I don't think my error handling method is nonsense as it seems to work for all the code I write. If I don't handle an error at local function scope, my intent is to handle it higher up the stack.
- dbaupp 10y agoYou're being too enthusiastic/wrong: there is a cost to Result and Option even in the good case. There still has to be a branch to check if there actually was no error. Result and Option are similar to error codes in C and have a similar cost/benefit trade-off, although of course with all the ADT/type-safety goodness.