News for hackers. Stuff that matters.
Re:Go 2 Draft Designs by conroy on Tuesday August 28, 2018 @12:17PM (#17860024)
Old and busted:

    func printSum(a, b string) error {
        x, err := strconv.Atoi(a)
	if err != nil {
		return err
	}
	y, err := strconv.Atoi(b)
	if err != nil {
		return err
	}
	fmt.Println("result:", x + y)
	return nil
    }
New hotness:

    func printSum(a, b string) error {
	x := check strconv.Atoi(a)
	y := check strconv.Atoi(b)
	fmt.Println("result:", x + y)
	return nil
    }
Re:Go 2 Draft Designs by reificator on Tuesday August 28, 2018 @12:32PM (#17860206)
Why the handler function over something like Rust's propagation operator? [0]

I see that it adds significant flexibility, but at the cost of verbosity and a new keyword that largely conflicts with one of Go's niches right now, web servers. And that flexibility seems likely to go unused in my experience. I would be shocked to see anything other than `return err` inside that block.

Sure errors are values and all that, and maybe I'm just working on the wrong codebases or following worst practices. But generally I see three approaches to errors in Go code, in order of frequency:

1. Blindly propagate down the stack as seen here. "It's not my problem- er, I mean, calling code will have a better idea of what to do here!"

2. Handle a specific type of error and silently recover, which does not typically need a construct like this in the first place.

3. Silently swallow errors, driving future maintainers nuts.

This seems to only really help #1, but `return thingThatCanError()?.OtherThing()?` can handle that just as well.

[0]: https://doc.rust-lang.org/book/second-edition/ch09-02-recove...

Re:Go 2 Draft Designs by DannyBee on Tuesday August 28, 2018 @01:17PM (#17860673)
I believe this is explicitly discussed in the doc: https://go.googlesource.com/proposal/+/master/design/go2draf...
Re:Go 2 Draft Designs by bvinc on Tuesday August 28, 2018 @03:25PM (#17862043)
Their discussion of Rust's error handling is woefully incomplete. They seem to think that the only thing that exists is the ? operator, which will return your error early, or a match statement, which is verbose. It misses some important aspects of rust's error handling.

1. There is a typed conversion between one type of error and another, which can keep context. 2. There are traits which can extend error types. 3. Backtraces can optionally be kept on (at a performance hit during errors of course).

I have a lot of code written that looks like this, using the excellent "failure" crate, which allows optionally to add context to any error:

file.seek(SeekFrom::Start(offset)) .context(format!("failed to seek to {} in file", offset))?;

You get explicit errors, minimal code, optional error context. It's pretty perfect.

Re:Go 2 Draft Designs by gnuvince on Tuesday August 28, 2018 @10:24PM (#17864937)
I got turned off of failure when a reddit user reported that it slowed down his program by 300x and the author of failure's response was that the original poster had misused failure.

https://www.reddit.com/r/rust/comments/7te8si/personal_exper...

Re:Go 2 Draft Designs by Dylan16807 on Wednesday August 29, 2018 @12:13AM (#17865564)
They said the OP misused it largely because the documentation was insufficient, which is a reasonable response. There are different types of error object, and the one used was a poor fit for common, expected errors.

What would you have wanted them to do instead?

Re:Go 2 Draft Designs by reificator on Wednesday August 29, 2018 @07:54PM (#17873074)
That's unfortunate, as I think you're reading into it to see an attitude that is not there. In fact that response makes me more interested in using it.

The author starts by identifying the use of the wrong type. They then immediately blame it on their own documentation in the very next sentence.

Re:Go 2 Draft Designs by reificator on Tuesday August 28, 2018 @09:37PM (#17864611)
That snippet looks like it'd be very helpful, I'm going to make a note to check out that library. Thanks!
Re:Go 2 Draft Designs by DannyBee on Tuesday August 28, 2018 @05:05PM (#17862876)
"Their discussion of Rust's error handling is woefully incomplete."

These are clearly not intended to be dissertations on the topic.

However, i don't think anything you said changes their discussion, actually.

Re:Go 2 Draft Designs by stevenhuang on Tuesday August 28, 2018 @08:25PM (#17864119)
> However, i don't think anything you said changes their discussion, actually.

How does it not? It seems to me that OP revealed an alternative method of error handling (which is very cool I might add), exactly in response to Google's discussion on Rust's error handling.

If the discussion doesn't address this, then it seems reasonable to say it's incomplete, as its omission makes it appear they're not debating Rust at its strongest.

edit: paper->discussion

Re:Go 2 Draft Designs by DannyBee on Thursday August 30, 2018 @01:24PM (#17878643)
Because none of the things said actually refute any of the points in the Google discussion? At all?

I also definitely don't feel a failure to go off and survey every random library that exists for rust makes a discussion "woefully incomplete" or that it isn't debating rust at it's strongest. The same treatment is given to all languages, including rust, go, etc.

The discussion is also about language features, and it discusses the language features.

Re:Go 2 Draft Designs by jerf on Tuesday August 28, 2018 @12:35PM (#17860245)
In your new hotness, I believe that handler is already implicit, so you can just leave it off.

What I am still curious about, and can't see whether or not is possible from the current draft (possible I've just skimmed too hard) is whether printSum can become:

    func printSum(a, b string) error {
        fmt.Println("result:",
            check strconv.Atoi(a) +
            check strconv.Atoi(b))
        return nil
    }
Possibly with an addition paren around the check.

(I'm just using this as an example; in this case I probably would assign x & y on separate lines since in this case this saves no lines, but there are other cases where I've wanted to be able to call a function that has an error but avoid the 4-line dance to unpack it.)

Re:Go 2 Draft Designs by cyphar on Tuesday August 28, 2018 @12:59PM (#17860498)
It can, and this is mentioned in the error chain design doc[1].

[1]: https://go.googlesource.com/proposal/+/master/design/go2draf...

Re:Go 2 Draft Designs by ianlancetaylor on Tuesday August 28, 2018 @12:57PM (#17860483)
Yes, what you write should work with the current draft.
Re:Go 2 Draft Designs by jerf on Tuesday August 28, 2018 @12:57PM (#17860491)
Cool. It isn't something I'd want to abuse, but there are places in my code where it would clean things up nicely, too.
Re:Go 2 Draft Designs by webkike on Tuesday August 28, 2018 @01:03PM (#17860538)
That should be possible because check is an expression
Re:Go 2 Draft Designs by millstone on Tuesday August 28, 2018 @12:37PM (#17860273)
Serious question: why is the error return from Println not handled?
Re:Go 2 Draft Designs by jerf on Tuesday August 28, 2018 @01:07PM (#17860575)
In general, catching errors when there is simply nothing useful to do with them isn't that useful. If the world is so broken that printing isn't working, it probably isn't the print failing that you care about. Even the errcheck linter, which I use in all my serious code, doesn't make you check the error from basic print statements like that. (Fprint it makes you check. But not just Print.)
Re:Go 2 Draft Designs by mike_hearn on Tuesday August 28, 2018 @02:38PM (#17861569)
Then why does Print return an error code at all?
Re:Go 2 Draft Designs by axaxs on Tuesday August 28, 2018 @02:51PM (#17861700)
So if the printing doesn't work, you can see what's going on?
Re:Go 2 Draft Designs by mike_hearn on Tuesday August 28, 2018 @02:56PM (#17861770)
But all the comments in response to the question are saying there's no point in checking the return code of print, because if it's broken, you can't print the error code you got anyway (or it's never broken in practice).

I think there's a deeper point the question asker was making here - it's easy to say "everyone should just check error codes" but in practice it never works out that way. People always ignore/swallow some of them, or propagate them in such a way that they lose detail e.g. a detailed error code from a subsystem is mapped to a generic error code higher up the abstraction stack. Print failing is a classic example of something most wouldn't care to check, but perhaps there's a good reason for it. With exceptions in the common case, where nobody checked, an exception would propagate up the stack until it was either caught and handled in a generic way ("subsystem X failed") or simply caught by the runtime.

Re:Go 2 Draft Designs by jerf on Tuesday August 28, 2018 @06:03PM (#17863327)
"But all the comments in response to the question are saying there's no point in checking the return code of print"

That's not quite what I said, and the difference is important. I said there's no point catching an error when you have nothing useful to do with it.

It so happens that the basic "print to standard out" is the extreme outlier of a function where you have nothing very useful to do with it in the general case. What are you going to do, print an error? Are you going to log it? Probably not, because if you have a log in the first place you probably aren't seriously using printing.

There are exceptions where you might care, such as a command-line program where printing is the major case you care about, but it so happens that the printing functions often have nothing useful to do with errors.

It is not the only case. I often don't log things terribly strongly during the time a socket is being shut down at some point in a protocol where both sides have agreed to shut the connection down, for instance. Or if I have a socket connection and the underlying connection fails, I don't need an individual log message for every concrete manifestation of that fact necessarily. If logging fails... uhh... what exactly is one supposed to do with that information in the general case?

So even though I use errcheck like I said on all my code, there are places where I throw the error away on purpose. It's clearly indicated in the code, though, rather than being implicit, since errcheck requires a "_ = erroringThing()" line of code. Except for printing to stdout, because as a special case, that's just too common a case to worry about, even when one is careful enough to use a linter for everything else.

Re:Go 2 Draft Designs by nomel on Tuesday August 28, 2018 @04:37PM (#17862636)
There's no reason to care about print being broken, unless there's a reason to care about print being broken. Both cases need to be supported. The developer ignoring the error value if they don't care is perfectly rational.
Re:Go 2 Draft Designs by jagger27 on Tuesday August 28, 2018 @01:43PM (#17860950)
I'm having a hard time making it fail. Even with Printf, in the output it tells you what you did wrong instead of returning an error.

In fact, the only _print function I could get to fail was Fprintf by sending an already closed file. I suppose if it was somehow not allowed to write to stdout the other functions might fail. But I'm not sure.

    _, err := fmt.Printf("Hello %d", "world")
    fmt.Println(err)
    
    >> Hello %!d(string=world)
    >> <nil>
Re:Go 2 Draft Designs by ianlancetaylor on Tuesday August 28, 2018 @04:10PM (#17862425)
Yes, the various fmt.Print functions only return an error if Write on the underlying Writer returns an error.
Re:Go 2 Draft Designs by TheDong on Wednesday August 29, 2018 @01:15PM (#17869932)
Here you go: https://play.golang.org/p/0vGdejrjm5H

To reproduce the key lines here:

    os.Stdout.Close()
    _, err := fmt.Println("foo")
You can also close stdout from outside the program, use ptrace to cause the write call to fail, or a number of other things.
Re:Go 2 Draft Designs by reificator on Tuesday August 28, 2018 @12:43PM (#17860345)
There are two main reasons:

1. Many developers learn the print funcs before they really start paying attention to errors as return values, and so never think to check what it returns.

2. If println is failing, things are going very wrong. It would probably be more ergonomic to just have it panic in most cases, because if it errors out then what are you honestly going to do to recover gracefully? Just let your surrounding infrastructure handle it, whether that's restarting the process, starting a new container somewhere else on the cluster, or whatever.

Re:Go 2 Draft Designs by skybrian on Tuesday August 28, 2018 @02:12PM (#17861271)
If stdout is unavailable, should that really be fatal? Perhaps it should be considered an unusual equivalent to piping to /dev/null.
Re:Go 2 Draft Designs by reificator on Tuesday August 28, 2018 @03:38PM (#17862161)
Which is the current practice of ignoring errors from `fmt.Println()`.
Re:Go 2 Draft Designs by grey-area on Tuesday August 28, 2018 @02:55PM (#17861749)
In this particular example there is no good reason, it’d be better to end with:

    return fmt.Println()
Re:Go 2 Draft Designs by spyspy on Tuesday August 28, 2018 @12:27PM (#17860159)
Only seems useful when you have a function and want every error handled the exact same way, and don't have any requirement to add context the way https://github.com/pkg/errors does.
Re:Go 2 Draft Designs by ianlancetaylor on Tuesday August 28, 2018 @12:29PM (#17860187)
Read the design draft (https://go.googlesource.com/proposal/+/master/design/go2draf...). The issues you raise are addressed.
Re:Go 2 Draft Designs by spyspy on Tuesday August 28, 2018 @01:39PM (#17860908)
Not quite. You'd need to write new `handle err { ... }` blocks for every new context, like how they have one in the for loop. In effect you're just moving the handling of the error away from the place it occurred, which is not optimal. As someone who writes Go every day I don't see this strategy effectively cutting down on boilerplate core or adding clarity.
Re:Go 2 Draft Designs by tracker1 on Tuesday August 28, 2018 @12:36PM (#17860255)
While this is true, more often than not, in lower-level functions you want errors to bubble up. For example, in most Node.js code, the first line in each callback raises the error to the callback, or likewise throws up in a promise/async chain.

Beyond this, there's no reason you can't add additional context and/or wrap the original error before returning your own error.

With this additional syntax and the addition of generics, I'm far more likely to take up go. Up to this point, I'd only toyed with it a little, because it just felt so cumbersome to actually get stuff done compared to node. Beyond that, dealing with web-ui, if I'm going to take on a cognitive disconnect for a back-end, I want as little friction as possible.

Re:Go 2 Draft Designs by dvlsg on Tuesday August 28, 2018 @01:50PM (#17861038)
I'm in the same boat as you. Error handling and code generation (to support the lack of generics) in go always left a poor impression on me. I ended up going back to node for work, f# or typescript for personal projects.

These two changes alone are enough to get me to pick go back up.

Re:Go 2 Draft Designs by spyspy on Tuesday August 28, 2018 @01:42PM (#17860938)
My point is that using this to wrap context around errors is even more verbose than what's available/common today, since you either settle for one wrap for the whole function or you have to define multiple `handle` blocks.
Re:Go 2 Draft Designs by tracker1 on Tuesday September 04, 2018 @01:33PM (#17910982)
Does the "old" way no longer work?