Live data from Hacker News

Gopher Wrangling: Effective error handling in Go

stephenn.com

91–100 of 310 posts

Re: Gopher Wrangling: Effective error handling in Go

#91
Could someone explain why is Go so hyped?

In my personal opinion it is just not a good language, and I think many judge it based on some false basis that it is somehow “close to the hardware” because it produces a binary. Like, the amount of time it is put next to Rust when the two have almost nothing in common..

It is very verbose, yet Java is the one that is called that, often by Gophers, which is much more concise. It has terrible expressivity, a managed language which is a perfectly fine design choice, yet seemingly every other language with a GC is somehow living in sin.

And still, it doesn’t fail to show up each day on HN.

Re: Gopher Wrangling: Effective error handling in Go

#92

Surprised that the "always wrap your errors" rule isn't in there. It's been the rule in the last few Go teams I've been in.

It seems like a waste of time to me. Wrapping errors adds context. But you can usually get enough context from stack traces.

Re: Gopher Wrangling: Effective error handling in Go

#93

Earlier quoted context omitted.

The biggest difference is in rust you have to handle the error case, but in go you can accidentally ignore it.

This seams silly and nitpicks to my. In go, you have to assign the error, and if you assign it you have to use the variable. I've never seen this mistake before in my years of using go.

> In go, you have to assign the error, and if you assign it you have to use the variable.

Nope. Go errors on dead variables, not on dead stores.

And because idiomatic go tends to reassign errors to the same variable if you have multiple error-returning functions in the same scope any one of them being checked will make the compiler happy.

You also do not have to assign the error at all, you can just ignore the entire thing.

Re: Gopher Wrangling: Effective error handling in Go

#94
post #86

Earlier quoted context omitted.

Result covers the very common case where there is T or error but never both. Having a decent type system doesn't prevent you from returning both. In Go I simply cannot have something as simple as "A or B, but never both or neither" as a type. >However, Go believes you cannot make that assumption. This is trying to twist a weakness of Go's type system as a virtue. In Go I always have "A or B, including neither and bot…

> This is trying to twist a weakness of Go's type system as a virtue. No. There is no discussion about type systems taking place here at all. The discussion is about patterns where the producer or the consumer is in control. Specifically, Result puts the producer in control. Idiomatic Go (T, error) sees the consumer in control. There is likely no language in existence that prevents you from choosing. You can write co…

This entire discussion is about the type system and how it interacts with the language. Go does not have sum types, so the producer cannot signal to the consumer what it can produce and what the consumer has to handle. The consumer in Go must pessimistically assume the all combinations of return values are possible, where in a language with a decent type system the possible cases can be enumerated and handled appropriately.

>Idiomatic Go sees the caller in control.

This is simply not true. If the producer never returns A+B, then the consumer is never going to magic that into existence. The producer is always in control of what the producer returns.

>Specifically, Result puts the producer in control.

Result is just a convenience type for the very common case of a function returning "A or B, but never both or neither." If your producer does not match that, you are not forced by the language to use Result. In Go, you are always forced by the language to return a value of type "A or B, including both or neither." The consumer isn't any more in control, it just has to guess whether "both or neither" are possible return types. In Rust/Haskell/Ocaml/Java, hell even C++ with variant or C with union, I can return something or with a more restrictive type. Honestly Go is uniquely incapable of representing these cases in its type system and control flow.

Re: Gopher Wrangling: Effective error handling in Go

#95

The provided examples highlight exactly why error handling in golang is verbose, error prone, and lacks context. Do people really not care about stack traces?

Less than I thought I would. I work with a very large Go codebase, and I don't remember the last time I had problems because I needed a stack trace. Just grepping for the error message is enough to show me exactly where it happened. Still, this doesn't mean that Go does not have stack traces. It does have stack traces for panics, and you can create stack traces by wrapping errors.

I frequently am sad about the kind of Rust error that doesn't have stack traces. Do you not often see something like "file not found" and need to know what file wasn't found? Or do the lowest level go error types carry more context?

Re: Gopher Wrangling: Effective error handling in Go

#96

Surprised that the "always wrap your errors" rule isn't in there. It's been the rule in the last few Go teams I've been in.

It seems like a waste of time to me. Wrapping errors adds context. But you can usually get enough context from stack traces.

But you don’t have stacktraces.

Re: Gopher Wrangling: Effective error handling in Go

#97

Earlier quoted context omitted.

Less than I thought I would. I work with a very large Go codebase, and I don't remember the last time I had problems because I needed a stack trace. Just grepping for the error message is enough to show me exactly where it happened. Still, this doesn't mean that Go does not have stack traces. It does have stack traces for panics, and you can create stack traces by wrapping errors.

I frequently am sad about the kind of Rust error that doesn't have stack traces. Do you not often see something like "file not found" and need to know what file wasn't found? Or do the lowest level go error types carry more context?

IIRC os.Open includes the pathname of the file it’s operating on. But still, if you don’t wrap your errors, you get useless errors which are unlocatable. Wrapping is essential in go. Thankfully the STL has good facilities for adding relevant data (like pathnames) to your wrapping

Re: Gopher Wrangling: Effective error handling in Go

#98
post #91

Could someone explain why is Go so hyped? In my personal opinion it is just not a good language, and I think many judge it based on some false basis that it is somehow “close to the hardware” because it produces a binary. Like, the amount of time it is put next to Rust when the two have almost nothing in common.. It is very verbose, yet Java is the one that is called that, often by Gophers, which is much more concise…

1. It's opinionated, so there's often only one way of doings things. Largely, the "one way" is a good way, so people appreciate the forced consistency 2. It's simple. It is very easy to read and write. It is hard to shoot yourself in the foot. 3. It's powerful. They have a few core abstractions that compose well (generic io, http stuff). 4. It's fast. It runs fast because it's compiled, and it compiles fast because it's simple.

Me personally: I appreciate the simplicity of it. It's a great language for working with in a team. I wish it was more functional, and had better ways to handle errors, but the simplicity of it all was a breath of fresh air using it in a working environment.

Re: Gopher Wrangling: Effective error handling in Go

#99
post #69

Earlier quoted context omitted.

This doesn't really make much sense. The producer knows what it's returning. In the _vastly_ common case, it's either returning an error or a success object, but the Go type system is unable to represent that. The caller trying to pretend that the success object is there isn't a freedom the caller gets in the current system, it's an artifact of the type system not being powerful enough to encode the situation accurat…

> The producer knows what it's returning. But doesn't know how the return values will be used by the caller. What is perhaps lost in this is where Go says that values should always be useful? > If you have a function that can return both an object and an error, there still should be a way to represent that (exactly the current way). Exactly the current way is what is said to be deficient, though. A function of this t…

> But doesn't know how the return values will be used by the caller. What is perhaps lost in this is where Go says that values should always be useful?

It doesn't matter how it will be used by the caller. If I'm writing a function that can fail, no magic in existence can create a success object out of nothing, especially one that "should always be useful". At that point you're stuck either returning a nil pointer or a zombie object (along with the error).

> Exactly the current way is what is said to be deficient, though.

It's deficient because it's modelling the wrong (in the common case) thing. I'm saying if you're in the uncommon case and that actually _is_ what you're trying to model, then you still can.

> A function of this type is naturally going to return a file every time because a file is always useful, even when there is failure.

What? No. It's not useful if there is no file, if the error is "wtf, that file doesn't exist".

Re: Gopher Wrangling: Effective error handling in Go

#100
In the last example, it is preferable to use `if errors.Is(err, context.DeadlineExceeded) {...}` instead of the given `if err == context.DeadlineExceeded {...}` since the `errors.Is()` function will recursively unwrap error chains to find the specified error.

https://go.dev/blog/go1.13-errors

Post reply on HN