If a programming language supports union types and type inference then there is no need for such a "special" language feature as described here. The return type will simply have the form "goodresult | error" and the compiler will infer it based on the function-body alone.
At the same time, the following problem of zig is automatically a non-issue:
> A serious challenge with Zig's simple approach to errors that our errors are nothing more than enum values.
If the result is "goodresult | error" then I can choose of what type "error" is and I'm not bound to any predefined types that the language forces upon me.
In fact, the article then shows how to work around the issue with a tagged union. The problem is that this creates another issue: the composition of different error types.
E.g. having function foo calling bar and baz and bar can fail with barError and baz can fail with bazError. Then, without having to do anything, the compiler should infer that foo's return type should be "goodresult | barError | bazError". This won't work if tagged unions are used like in the example - they will have to be mapped by hand all the time, creating a lot of noise.
While Zig's errorhandling is not bad and miles above Go's, it makes me sad to see that we still lack ergonomy of errorhandling in so many languages despite knowing pretty much all of the language-features that are necessary to make it much easier.