Live data from Hacker News

Gopher Wrangling: Effective error handling in Go

stephenn.com

291–300 of 310 posts

Re: Gopher Wrangling: Effective error handling in Go

#291

Earlier quoted context omitted.

Uh? If you use `%w` in fmt.Errorf(), it should still work with .Is and .As?

If your original error is `fmt.Errorf("failed to frobulate")` or even `errors.New("failed to frobulate")`, even if it's bubbled up with `fmt.Errorf("foo() failed: %w", err)`, errors.Is and errors.As are useless for checking it (each error in the chain, including the original, will be a new instance of fmt.stringError or something like that). This is equivalent to the problem of throwing new Exception("some message")…

You are wrong: https://go.dev/play/p/6PTQk0xWAAl

Re: Gopher Wrangling: Effective error handling in Go

#292
post #275

Earlier quoted context omitted.

Uh? If you use `%w` in fmt.Errorf(), it should still work with .Is and .As?

> Uh? If you use `%w` in fmt.Errorf(), it should still work with .Is and .As? You still need a stable error to compare against in errors.Is. fmt.Errorf is not going to provide that if the message is dynamic - which is almost always.

I think what you're talking about with "stable" and "dynamic" are error constants (like `io.EOF`) and instances of error types (like `url.Error` https://pkg.go.dev/net/url#Error). You use `Is` for the former and `As` for the latter; fmt.Errorf's %w directive doesn't inhibit either case.

Re: Gopher Wrangling: Effective error handling in Go

#293

Earlier quoted context omitted.

I agree that there is room for improvement, but I don’t mind Go’s errors that much. Using a linter to make sure errors are checked doesn’t seem like a major problem (you have to run a linter anyway, so what’s the harm?); most Go developers reflexively check errors for everything besides fmt.Println anyway. It would be better to put this in the compiler I suppose, but not a major deal. Also worth noting that Rust does…

Unfortunately, fmt.Errorf makes errors.Is/As useless. In fact, errors.Is is mostly useless in general, since very few Go libraries have any error types at all. You're usually stuck with parsing error messages if you actually want to handle errors programmatically, even for much of the standard library.

As others have mentioned, this isn't true. `errors.Is` is for named error instances (like `io.EOF`) while `errors.As` is for matching against a particular type of error (like `net/url.Error`). `fmt.Errorf()` doesn't inhibit either case.

https://go.dev/play/p/zsks73MGZjc

Re: Gopher Wrangling: Effective error handling in Go

#294

Earlier quoted context omitted.

nope! go's error handling is actually good! it turns out that treating errors the same as normal values makes programs more reliable lots of people get salty about it, for sure

The _right_ way to treat them as normal values is by using sum types . So no, Go's error handling isn't at all good. 1.13 might've made them less execrable, but it didn't make it good.

you know i looked into it and it turns out that there is no actual consensus on what "the _right_ way" to treat errors is! huh! how about that

Re: Gopher Wrangling: Effective error handling in Go

#295

Earlier quoted context omitted.

true! of course.

I'm not sure we are in full agreement, my statement was a little ambiguous. I meant to say that, just like stack traces, Go error messages are only helpful to developers, at least 99.9% of the time. More generally, user error messages and dev error messages are just fundamentally at odds, there is no way to have messages that are good for both cases. User error messages should explain what went wrong, and what they c…

nah

go error messages are clear to users without being cryptic

Re: Gopher Wrangling: Effective error handling in Go

#296

Earlier quoted context omitted.

? enables chaining, chaining subverts comprehensibility in exactly the ways i'm describing

All languages have chaining: that's what `;` is for. Chaining together transitions in an imperative state machine isn't simpler than chaining together `Result`s, you're just used to it.

chaining means combining a sequence of expressions that each take the same input as they give as output

; doesn't do this, afaict

when you're writing imperative code it's important that control flow (return) is explicitly visible

Re: Gopher Wrangling: Effective error handling in Go

#297
post #162

Earlier quoted context omitted.

That's what the parent comment means, I think.

I didn't mean anything in particular, I just made an observation. I feel bad for having all those unchecked error returns. I wouldn't want to wrap them in a panic, especially if it was some kind of server. Actually, I'm reminded of certain errors I've seen along the lines of "exception raised while handing exception".

the question is: does it matter if a given expression fails?

if so, get the error and evaluate it -- like if json.Marshal fails in your http.Handler

if not, (shrug) -- like (maybe) if your fmt.Printf fails

panics are for core assertion violations, not an ersatz error reporting mechanism

Re: Gopher Wrangling: Effective error handling in Go

#298

Earlier quoted context omitted.

if we say a language "addresses" a given concern, is it necessary that this is accomplished in the compiler, and that the rules for that concern, whatever they are, are enforced at compile-time? (spoiler: no)

spoiler: yes If the language "addresses" it by convention then it is not addressing it at a language level at all

"the language level" is not only what is defined and enforced by the compiler

but i'm sure i won't convince you of anything here, so good luck to you

Re: Gopher Wrangling: Effective error handling in Go

#299

Earlier quoted context omitted.

All languages have chaining: that's what `;` is for. Chaining together transitions in an imperative state machine isn't simpler than chaining together `Result`s, you're just used to it.

chaining means combining a sequence of expressions that each take the same input as they give as output ; doesn't do this, afaict when you're writing imperative code it's important that control flow (return) is explicitly visible

> chaining means combining a sequence of expressions that each take the same input as they give as output

> ; doesn't do this, afaict

It's chaining together transitions in the state machine that is your program.

> when you're writing imperative code it's important that control flow (return) is explicitly visible

Control flow is visible with `?` or other do-notation variants. If I want to error out in a `Result` context, I explicitly return `Err(bad stuff)`. And if I don't, I explicitly return `Ok(return value)` instead. If I want to introduce a new asynchronous value in js, I explicitly call `new Promise`. And so on.

What's not visible in a do-block is the implementation of control flow. Which is fine, because this isn't the code that controls it - `Ok(Err(x))` is reduced in exactly the same way no matter what `x` is or where it came from. Traditional imperative code is the same way: the runtime system always works the same way, no matter which statements you ask it to execute.

If you do choose to expose the underlying mechanisms of your control flow everywhere, you get continuation-passing style, which is useful in small doses but more or less impossible to reason about at scale.

Re: Gopher Wrangling: Effective error handling in Go

#300

Earlier quoted context omitted.

I'm not sure we are in full agreement, my statement was a little ambiguous. I meant to say that, just like stack traces, Go error messages are only helpful to developers, at least 99.9% of the time. More generally, user error messages and dev error messages are just fundamentally at odds, there is no way to have messages that are good for both cases. User error messages should explain what went wrong, and what they c…

nah go error messages are clear to users without being cryptic

How is an error like "Error in foo(): error in bar(): reading abcd.xml: file not found" clear to a user? What do foo() and bar() mean to them? What should they do about this missing file? This is just a more simply formatted stack trace, just as useless to an end user.

The proper error message for a user in this situation would be something like "Couldn't read required file /home/user/program-name/abcd.xml. Please try to create the file by hand." or "Couldn't read list of entities. Try reinstalling the program or contact support@program-name.com".

Post reply on HN