News for hackers. Stuff that matters.
Re:(On | No) Syntactic Support for Error Handling by packetlost on Wednesday June 04, 2025 @01:04AM (#44177391)
> Unless documented otherwise, a non-nil error renders all other return values invalid, so there's no real sense of a "bogus value" alongside a non-nil error

But you have to return something to satisfy the function signature's type, which often feels bad.

>> Designing the error mechanism based on the assumptions that errors are primarily meant to be logged and that you have to get out of your way to develop errors that can actually be handled

> I don't see how any good-faith analysis of Go errors as specified/intended by the language and its docs, nor Go error handling as it generally exists in practice, could lead someone to this conclusion.

I agree to a point, but if you look at any random Go codebase, they tend to use errors.New and fmt.Errorf which do not lend themselves to branching on error conditions. Go really wants you to define a type that you can cast or switch on, which is far better.

Re:(On | No) Syntactic Support for Error Handling by kiitos on Wednesday June 04, 2025 @02:55AM (#44177876)
> Go really wants you to define a type that you can cast or switch on, which is far better.

Go very very much does not want application code to be type-asserting the values they receive. `switch x.(type)` is an escape hatch, not a normal pattern! And for errors especially so!

> they tend to use errors.New and fmt.Errorf which do not lend themselves to branching on error conditions

You almost never need to branch on error conditions in the sense you mean here. 90% of the time, err != nil is enough. 9% of the time, errors.Is is all you need, which is totally satisfied by fmt.Errorf.

Re:(On | No) Syntactic Support for Error Handling by packetlost on Wednesday June 04, 2025 @04:07AM (#44178327)
> Go very very much does not want application code to be type-asserting the values they receive. `switch x.(type)` is an escape hatch, not a normal pattern! And for errors especially so!

A type assert/switch is exactly how you implement Error.Is [^0]. Sure it's preferable to use the interface method in case the error is wrapped, but the point stands.

> You almost never need to branch on error conditions in the sense you mean here. 90% of the time, err != nil is enough. 9% of the time, errors.Is is all you need, which is totally satisfied by fmt.Errorf.

I'd argue it's higher than 9% if you're dealing with IO, which most applications will. Complex interfaces like HTTP and filesystems will want to retry on certain conditions such as timeouts, for example. Sure most error checks by volume might be satisfied with a simple nil check, it's not fair to say branching on specific errors is not common.

[0]: The documentations own example of implementing Error.Is uses a type switch. https://cs.opensource.google/go/go/+/refs/tags/go1.24.3:src/...

Re:(On | No) Syntactic Support for Error Handling by Yoric on Wednesday June 04, 2025 @04:32AM (#44178485)
> If you define errors with Errors.New you use string comparison.

With `Errors.New`, you're expected to provide a human-readable message. By definition, this message may change. Relying on this string comparison is a recipe for later breakages. But even if it worked, this would require documenting the exact error string returned by the function. Have you _ever_ seen a function containing such information in the documentation?

As for `switch x.(type)`, it doesn't support any kind of unwrapping, which means that it's going to fail if someone in the stack just decides to add a `fmt.Errorf` along the way. So you need all the functions in the stack to promise that they're never going to add an annotation detailing what the code was doing when the error was raised. Which is a shame, because `fmt.Errorf` is often a good practice.

Re:(On | No) Syntactic Support for Error Handling by packetlost on Wednesday June 04, 2025 @10:34AM (#44181135)
I was actually referring to the implementation of errors.Is, which uses string comparison internally if you use the error type returned by errors.New and a type cast or switch if you use a custom type (or the cases where the stdlib defines a custom error type).
Re:(On | No) Syntactic Support for Error Handling by kiitos on Wednesday June 04, 2025 @04:40PM (#44185268)
errors.Is definitely doesn't use string comparison internally:

https://cs.opensource.google/go/go/+/refs/tags/go1.24.3:src/...

Re:(On | No) Syntactic Support for Error Handling by masklinn on Thursday June 05, 2025 @01:08AM (#44188474)
Of course it does. What do you think an errors.New contains and how do you figure it compares by value when checking placeholder errors?

The answer is that errors.New just wraps the error message in an errorString struct, and the second line of `is` is a string comparison.

Re:(On | No) Syntactic Support for Error Handling by kiitos on Friday June 06, 2025 @12:14PM (#44202340)
Re:(On | No) Syntactic Support for Error Handling by masklinn on Saturday June 07, 2025 @05:09AM (#44208429)
Oh hell, I always forget equality doesn't go through pointers in go, you're right.

And as you demonstrate I could have tested it easily enough to confirm I was reading the code correctly...

Re:(On | No) Syntactic Support for Error Handling by Yoric on Friday June 06, 2025 @01:45AM (#44198105)
My bad, I misunderstood your previous comment.
Re:(On | No) Syntactic Support for Error Handling by kiitos on Wednesday June 04, 2025 @06:42AM (#44179283)
> A type assert/switch is exactly how you implement Error.Is [^0]

errors.Is is already implemented in the stdlib, why are you implementing it again?

I know that you can implement it on your custom error type, like your link shows, to customize the behavior of errors.Is. But this is rarely necessary and generally uncommon..

> If you define errors with Errors.New you use string comparison, which is only convenient if you export a top level var of the error instead of using Errors.New directly.

What? If you want your callers to be able to identify ErrFoo then you're always going to define it as a package-level variable, and when you have a function that needs to return ErrFoo then it will `return ErrFoo` or `return fmt.Errorf("annotation: %w", ErrFoo)` -- and in neither case will callers use string comparison to detect ErrFoo, they'll use errors.Is, if they need to do so in the first place, which is rarely the case.

This is bog-standard conventional and idiomatic stuff, the responsibility of you as the author of a package/module to support, if your consumers are expected to behave differently based on specific errors that your package/module may return.

> Complex interfaces like HTTP and filesystems will want to retry on certain conditions such as timeouts, for example. Sure most error checks by volume might be satisfied with a simple nil check, it's not fair to say branching on specific errors is not common.

Sure, sometimes, rarely, callers need to make decisions based on something more granular than just err != nil. In those minority of cases, they usually just need to call errors.Is to check for error identity, and in the minority of those minority of cases that they need to get even more specific details out of the error to determine what they need to do next, then they use errors.As. And, for that super-minority of situations, then sure, you'd need to define a FooError type, with whatever properties callers would need to get at, and it's likely that type would need to implement an Unwrap() method to yield some underlying wrapped error. But at no point are you, or your callers, doing type-switching on errors, or manual unwrapping, or anything like that. errors.As works with any type that implements `Error() string`, and optionally `Unwrap() error` if it wants to get freaky.

Re:(On | No) Syntactic Support for Error Handling by stouset on Wednesday June 04, 2025 @03:47AM (#44178208)
> 90% of the time, err != nil is enough

Only if your only desire is to bubble the error up and quite literally not handle it at all.

If you want to actually handle an error, knowing what actually went wrong is critical.

Re:(On | No) Syntactic Support for Error Handling by kiitos on Wednesday June 04, 2025 @06:59AM (#44179372)
Returning an error -- or, more accurately, identifying an error and returning an annotation or transformation of that error appropriate for your caller -- is a way of handling it. The cases where, when your code encounters an error, that it can do anything other than this are uncommon.
Re:(On | No) Syntactic Support for Error Handling by stouset on Wednesday June 04, 2025 @01:28PM (#44183238)
This goes completely against the golang error-handling mindset.

Error handling is so important, we must dedicate two-thirds of the lines of every golang program to it. It is so important that it must be made a verbose, manual process.

But there's also nothing that can be done about most errors, so we do all this extra work only to bubble errors up to the top of the program. And we do all this work as a human exception-handle to build up a carefully curated manual stack trace that loses all the actually-useful elements of a stack trace like filenames and line numbers.

Re:(On | No) Syntactic Support for Error Handling by tinthedev on Thursday June 05, 2025 @07:53AM (#44190717)
This is just nonsense.

Handling errors this way is possible in only very brittle and simplistic software.

I mean, you're contradicting your very own argument. If this was the primary/idiomatic way of handling errors... then Go should just go the way of most languages with Try/Catch blocks. If there's no valuable information or control flow to managing errors... then what's the point of forcing that paradigm to be so verbose and explicit in control flow?

None.