On 6/4/22 6:56 PM, kdevel wrote:
> On Saturday, 4 June 2022 at 16:55:31 UTC, Steven Schveighoffer wrote:
[...]
> The point of an Error
is that your code can assume it cannot happen. If it does happen, the code is invalid.
According to my favorite dictionary "assume" means "take for granted" [1]. If Error
s may happen how can code (or its author) "assume" that Error
s cannot happen?
You don't assume it, you guarantee it. You are expected to provide a guarantee to the compiler that your code won't throw these errors.
But you aren't perfect, and so maybe you make a mistake, and trigger an Error. The compiler handles this unexpected condition by unwinding the stack back to the main function, printing the error and exiting, so you can go fix whatever mistake you made.
It's kind of like a segfault. There's no valid reason to read memory you don't own (yes, I know you can use segfaults to trigger loading of memory, I'm not talking about that kind of segfault). So what do you do when an unexpected segfault happens? You crash the program, and exit. In this case, the language is giving you by default a hint about where it occurred, and if you desire, you can get more information by catching the error where you want and doing more checks, etc.
> > This is reflected in the fact that the compiler will omit cleanup code if an Error
is thrown (it can assume that it will never happen).
But instead the compiler should emit the cleanup code and we would not have to discuss here carefully avoiding to name the root cause of all this entanglements.
A compiler could do this, and in fact, the compiler used to do this. But I think you are still not supposed to continue execution. I'm not sure what a compiler might assume at this point, and I unfortunately can't find in the language specification where it states this. It might not be in there at all, the spec is sometimes lacking compared to the implementation.
However, I did ask Walter about this last beerconf, and he said to treat a throw/catch of an error like a goto, anything can happen.
> > The point of using Error
is for a last resort check for program correctness (because you failed to validate the input before getting to that point).
If the code threw an Exception
instead of an Error
everything would be fine.
I think the point of Errors is that you can remove them for efficiency. In other words, just like asserts or bounds checks, they are not expected to be part of the normal working program. Exceptions are part of the program, and provide a different mechanism of handling error conditions.
> > [...]
A great example are range functions. Often times you see at the beginning of any popFront
method the statement assert(!empty);
. This is universally accepted, as you shouldn't be calling popFront
if you haven't checked for empty
.
Yep.
core.exception.AssertError@[...]linux/bin64/../../src/phobos/std/range/primitives.d(2280): Attempting to popFront() past the end of an array of int
I see no difference to the potentially invalid array index case. It would ease the use of the range if it threw an Exception
.
But it's possible to turn off asserts and make the code run faster. I personally never turn them off on certain programs (web server) because the penalty is not noticeable enough. But if these were Exceptions, they could not be turned off.
Consider the normal flow of a range in a foreach loop, it's:
// foreach(elem; range)
for(auto r = range; !r.empty; r.popFront) {
auto elem = r.front;
}
If both popFront
and front
also always call empty
you are calling empty
3 times per loop, with an identical value for the 2nd and 3rd calls. Having the assert allows diagnosing invalid programs without crashing your program, but also allowing full performance when you want it.
Phobos' RedBlackTree
has an invariant
which walks the entire RBT and validates the red-black property holds before and after every method call. This is not what you would want for performant code as it completely destroys the complexity guarantees. Yet it's there to help diagnose problems with RBT if you are working on modifying it. These kinds of checks are to help the developer prove their code is correct without having to continually prove it's correct for normal use.
-Steve