The solution to "Error handling"...
Dennis
dkorpel at gmail.com
Mon Jul 6 12:00:29 UTC 2026
On Sunday, 5 July 2026 at 21:57:55 UTC, Meta wrote:
> Rather than have his program crash when it tries to dereference
> a null pointer, he wants to paper over it and keep going like
> nothing's wrong. A segfault or an assert triggering is the
> correct behaviour in this case;
I have to admit that I find his example of a nil-struct a bit
abstract. But to me, the takeaway is not "replace null checks
with nil structs and pretend everything is fine now". The idea is
that you rethink your design so the whole concept of `null`
wasn't there in the first place.
In math, the product of an empty set is defined as 1. This is a
bit jarring at first, but in practice it works out beautifully in
formulas and prevents discrete cases. A programmer might be
tempted to implement `throw new Exception("at least 1 number
expected as input for product()")` and impose unnecessary
branches for 'error handling', when there was never a need for an
'error' to begin with.
In Java, an ArrayList can be null, forcing null checks like:
```Java
if (xyzList != null && xyzList.size() > 0) doSomething();
```
https://stackoverflow.com/questions/29784314/can-an-arraylist-be-null-and-have-a-size-0
In D, you can always check `array.length`. It's designed so that
whenever `array.ptr` is null, `array.length` is also 0 (save for
`@system` shenanigans), defining the null error out of existence
even when the variable is 0-initialized. I love that design!
> What if he wrote a text editing program that uses this
> philosophy? When the user wants to save their work, and fopen
> returns such a "nil struct" instead of a null pointer, it would
> appear to the user that their file was saved to disk, only for
> it to be completely lost once the program exits.
Defining errors out of existence is a hard sell, because it's not
a single pattern that you universally apply. It's a case-by-case
thing that requires real context and critical evaluation of your
entire stack. This is indeed a case where plainly applying the
'nil struct' pattern is a bad idea. Instead, you could question
why your application is 'opening a file' in the first place and
why it would have the responsibility to error check.
What if perhaps, your application would hand off the 'save data'
to the OS/browser/ui framework/whatever, and make it their
responsibility to write it to disk and report errors back to the
user? Notice how in this shell command, it's zsh reporting the
error:
```
echo "hello" > dummy/dummy.txt
zsh: no such file or directory: dummy/dummy.txt
```
echo simply writes to stdout without doing fopen itself, so it
doesn't have to do this error check. I know this doesn't define
the error literally out of existence, it just makes it someone
else's problem. But you can imagine if you have 100 shell
programs like this, that's now 99 less error checks.
As an aside, notice how when you
`fopen("chain/of/directories/file.txt")`, you get to check 1
error. But behind the string API, conceptually you are doing
`get("chain").get("of").get("directories").get("file.txt")`.
Each of these can fail, so you could also handle 4 different
error branches.
Now compare this snippet from the article:
```C
Node *n1 = ChildFromValue(root, 1);
Node *n2 = ChildFromValue(n1, 2);
Node *n3 = ChildFromValue(n2, 3);
Node *n4 = ChildFromValue(n3, 4);
```
Do you see the resemblance? 🙂
> Also, who unironically thinks errno is a good way to handle
> errors in 2023?
He said "there are reasonable aspects of its design". It being a
global variable and only a single integer of information is
certainly crappy. But I personally do agree with the principle
that actions should prefer producing result data AND error data
instead of result data OR error data. I personally implement this
by returning the result, and putting error data in an `ErrorSink`
parameter.
More information about the Digitalmars-d
mailing list