3 ms·
To be fair, Rust doesn't solve the errors-in-destructors thing any better: Drop::drop can't return anything.
by dbaupp 9y ago
To be fair, Rust doesn't solve the errors-in-destructors thing any better: Drop::drop can't return anything.
- vvanders 9y agoIt's not that hard to work around that, you just pass a reference in on creation that gets filled on drop(). The exception case is much nastier and a lot less explicit.
- dbaupp 9y ago"Just" passing a reference elides how clunky and annoying it is: depending on the code, it requires extra nested scopes, along with remembering to actually handle the error. Even the case of propagating the error upwards requires a bunch of annoyance: let mut failed = Ok(()) { let object = create_it(..., &mut failed); // ... } // destructor runs failed?; C++11 introduces a pile of tools for first-class manipulation of exceptions as values (the <exception> header), that mean that even using exceptions, the handling in a destructor can be fairly similar to your suggestion for Rust (e.g. wrap the contents of the destructor in a try/catch, and then fill in an exception_ptr& using current_exception()). The main difference is type erasure and allocation in C++. In any case, in either language, an object storing a reference to fill in later restricts how the object can be used, since it can no longer escape from the stack frame which owns the data to which the reference points.
- vvanders 9y agoExcept every exception in C++ is unchecked. Literally every API call can throw the entire world at you and you won't know it until you run the code under each permutation. This includes library code that you may not have the source for. Compared to the above I at least know from an API signature that there's the possibility of an error.
- dbaupp 9y agoYou're jumping around: exceptions being checked or not is orthogonal to returning errors from a destructor. It's true that being unchecked may make it harder to know when you need to, but the point stands that the ability to return errors from a destructor is not a way in which Rust's error handling is better than C++'s. Additionally, the C++ solution for returning errors from destructors is also explicit in the signature. (To be clear, I'm very firmly a fan of Rust, but I'm also a fan of accurate representations of it.)
- vvanders 9y agoFine, I'll bite then(although I think my point still stands). According to the spec current_exception() returns a smart pointer which the storage isn't specified. Part of that is implied allocation(returning bad_alloc for instance) which means you may be making a heap allocation for a pointer that could have been handled on the stack. That's going to rule out cases like the embedded space and gamedev where allocations during runtime is a big no-no. I'm no stranger to C++ but I've been bitten so many times by exceptions. Either in libraries that didn't say they'd throw them(but then did), ABI pollution and having to get new binary drops from vendors because turning them on bloated RTTI which caused binary sizes that didn't fit. They really have no place if you plan to use the language at any sort of scale.
- dbaupp 9y agoI think you're still looking at too much of the picture: this discussion is specifically about error handling in destructors (and how this affects finally/scope(exit)). There's numerous reasons why the checked, explicit, first-class, typed error handling of Rust is better than C++'s exceptions, but "able to use an outpointer to return an error from a destructor" isn't one of them: 1. using the outpointer is clunky and error prone 2. C++ can do the same thing (both with exceptions enabled, and with them disabled, the latter of which is pretty much the same as Rust) (The current_exception stuff even makes it possible to handle it fairly generically in C++, so even calling arbitrary code with exceptions-on can be the normal code wrapped in catch_and_write_exception(&exceptionSlot, [&] { ... });, which works for any library code, whether it throws or not.) > According to the spec current_exception() returns a smart pointer which the storage isn't specified Exceptions often allocate anyway; current_exception() isn't creating a new problem.