HN user

joeshaw

739 karma

https://joeshaw.org https://twitter.com/joeshaw https://github.com/joeshaw

[ my public key: https://keybase.io/joeshaw; my proof: https://keybase.io/joeshaw/sigs/AGXxr0IGC2jnOAVfKsaUq_-D8FtMdgQjctgtTFC8WSE ]

Posts9
Comments103
View on HN

This is a contrived example, but imagine a situation where I have a file I want to write on disk and then have a reference to it in a database. If I have a flow like:

    func UpdateUser(user *User) (err error) {
        f, err := os.Create("/some/file.txt")
        if err != nil {
            return err
        }
        defer CheckClose(f, &err)

        if _, err := f.Write(somedata); err != nil {
            return err
        }

        if err := db.UpdateUser(user, "/some/file.txt"); err != nil {
            return err
        }

        return
    }

This function might have updated the user in the database with a new file despite the fact that `CheckClose` (defined up-thread) does check to see if the `Close` failed and returned an error. The calling code won't have known this has happened.

The core problem is that the error checking is not done soon enough, either because Go programmers are conditioned to `defer f.Close()` from nearly all example code -- most of it demonstrating reads, not writes -- or because they are handling the error, but only in a deferred function, not earlier.

A more correct way to do this would be:

    func UpdateUser(user *User) error {
        f, err := os.Create("/some/file.txt")
        if err != nil {
            return err
        }
        defer f.Close()

        if _, err := f.Write(somedata); err != nil {
            return err
        }

        if err := f.Sync(); err != nil {
            return err
        }

        if err := f.Close(); err != nil {
            return err
        }

        if err := db.UpdateUser(user, "/some/file.txt"); err != nil {
            return err
        }
    }
`Sync()` flushes the data to disk, and `Close()` gives a "last-chance" opportunity to return an error. The `defer f.Close()` exists as a way to ensure resource cleanup if an error occurs before the explicit `f.Close()` toward the end of the function. As I mentioned in an update to the post, double `Close()` is fine.

OP here. Another commenter pointed out that `errors.Join` didn't exist when I wrote this, but I wouldn't have changed my guidance if it had.

The core issue here is that you want to deal with errors as soon as possible. The async nature of writes to files makes this more challenging. Part of the purpose of this post was to inform people that you don't necessarily always see write errors at write-time, sometimes you see them at close-time and should handle them there. (And sometimes you don't see them at all without fsync.)

Even if you do handle errors in your deferred close, you may be taking other actions that you'd rather not have until the file i/o is completed, and this could leave your program in an inconsistent state. Side effects in Go are common, so this is a practical concern and it is hard to spot and debug.

I caution against this approach, as you are not really dealing with the error when it occurs. If the work you do after the defer has other side effects, you may have just gotten your application into an inconsistent state and it's very hard to see in code why this might be.

`defer` is really not well-suited for error handling, its benefit is mainly in resource cleanup where failure is impossible or doesn't matter. (This makes it fine for `Close` on read-only file I/O operations, and not so great for writes.)

I'm fine with vendoring going away as long as we can check these new modules into version control and have vgo use them. Adding a caching proxy adds too much infrastructure for most folks, and without it you can't have reproducible and available builds.

I like Go's distributed package imports (even if everyone just uses Github), but it means you're distributing single points of failure all over the place. Vendoring (whether source or modules) solves this problem.

In net/http, if a handler panics you don't want it to bring down your entire application. For more info, see https://golang.org/pkg/net/http/#Handler

The encoding/json package is interesting because it uses both panic() and recover() internally in a way more reminiscent to how exceptions are used in other languages (that is, for control flow).

For example, https://github.com/golang/go/blob/2596a0c075aeddec571cd658f7... panics, and https://github.com/golang/go/blob/2596a0c075aeddec571cd658f7... recovers it.

Both uses are quite uncommon, though.

I think there are plans to make an official debugger it just hasn't been prioritized.

There was a nascent project inside the Go team a couple of years ago (called ergo I think?) but I believe it was shelved in favor of Delve, which was much further along.

It's probably safe at this point to consider Delve the de facto standard Go debugger.

My explanation for why is the following sentence:

If a function could fail, the function must return an error as its last return value. The caller should immediately check for errors from that function.

In practice, in idiomatic code it is quite rare for a function that returns a `(* Something, error)` to return `(nil, nil)`, which are the cases where a nil pointer dereference would most often happen. The other case is not _immediately_ checking errors. I would say both of these cases are not idiomatic Go. (Yes, there are exceptions.)

The other cases where I've seen nil dereferences pop up most often:

(1) the zero value for maps is nil, and a nil map is mostly not usable. `m[50] = "foo"` will panic with a nil dereference if you've not initialized `m`. This is one of the most annoying things about Go.

(2) not checking for presence when pulling out of a map. If you have `map[string]* Something` and do `x := m["nonexistent"]` and try to use `x`, it'll blow up. Always use the `x, ok := m["nonexistent"]` form instead, and deal with stuff when `ok == false`.

(3) nil receivers. `var x *Something`, then `x.Method()` -- the `Method()` method _can_ deal when `x == nil`, but most code does not.

My reasons for citing C and Java:

C doesn't have multiple return values, and kinds of C code deals with errors differently. Lots of stuff just returns `NULL` and all too often things don't check for it. (`malloc()` is a perfect example of this -- it rarely fails but a _lot_ of code doesn't check for errors. Now extrapolate that to everything :)

For Java, it's because the try-catch control structure complicates program flow. If you have a large block of code in a try-catch, it is very easy for a different line of code to throw an exception than you expected, and as a result an object you attempt to dereference later is null and you get an NPE. There are ways to deal with this, of course, but in my experience most people are pretty lazy about the non-happy path.

EDIT: formatting. how can a site about programming have such terrible support for typing in code

Mono was started by Miguel at Ximian, and most of the Mono code was owned by Ximian. Ximian was bought by Novell and with it, all the Ximian-owned Mono code. (I don't remember if a CLA was required before or after Novell bought Ximian.)

The Novell-owned IP was either sold or licensed to Xamarin later.

It may correct itself as you zoom in. It seems to group geographic areas together in somewhat centralized locations to avoid having too many bubbles at a particular zoom level.

If you zoom in on the east coast of the US, for instance, jobs are grouped between NYC and Boston and the location of the bubble doesn't make much sense until you zoom in further and see them separated.

The app doesn't matter anymore. Twitter is the app now (and whomever else they license their points-of-interest service to). Consumers of their service likely feed back check-in-equivalent data to foursquare, which allows them to do trending and bolsters their database further.

I lament the loss of the app too. Checking in and becoming the mayor was the fun of the old app, and Swarm just doesn't do it for me. At this point I use it mostly as a log of new places I've been to so I can visit them again in the future, particularly while traveling.

Yes! I can't believe that nobody else has made this point.

I am as disappointed in the pivot of the app as everyone else, but it's hardly the sign of impending death the post (and many of these comments) make it out to be. 4sq built up an incredible points-of-interest database, and that is their real product.

By providing that POI data to other providers -- most notably Twitter -- they get much of the same data that checkins in their own app gave them. And Twitter is massively more popular than the Foursquare/Swarm apps ever was or ever will be.

I'm not sure what the market is like in Cleveland, but in Columbus there is a great demand for web developers. I work out of a coworking place there with several freelance web developers and I'd be happy to put you in touch with them if you're interested.

Use `goimports` as a save hook in your editor. It analyzes your code and inserts/removes import lines as needed.

Other than the occasional need to fix up an ambiguous import that it guessed incorrectly, I haven't touched import lines in my code in a year.

The DHCP attack vector is the one that is scariest to me. We know that dhclient on Linux is vulnerable, but the number of unpatched Linux machines on a public wifi network will likely be too small to be worthwhile to try to capture. Macs on the other hand are prevalent in places like coffee shops, and Apple is notoriously slow to patch security vulnerabilities in their operating systems. Has anyone done much analysis on OS X's vulnerability to this bug?

ITerm2 2.0 12 years ago

The issue I've always had with the tmux integration is that it doesn't support multiple sessions simultaneously. I am typically moshed into several hosts and running tmux on each of them. I'd love to have one window per session with tabs corresponding to tmux windows.

I'm a little surprised more people don't run into this more often. I've never seen the benefit in running tmux locally. Do most people only ever use tmux on one remote host?

Edit: it's been a while since I've tried it; apparently tmux integration doesn't work over mosh at all. So s/mosh/ssh/g above and it still applies.

Leaving Go 12 years ago

I posted a comment about this to the blog, but I released a tool which will generate type-specific source code from a generic definition. Code is at https://github.com/joeshaw/gengen

The abs example could be reduced to:

    import "github.com/joeshaw/gengen/generic"

    func abs(x generic.T) generic.T {
        if x < 0 {
            return -x
        }
        return x
    }
You could then generate different type-specific versions:
    gengen abs.go int32 | gofmt -r 'abs -> absInt32' > abs_int32.go
    gengen abs.go float64 | gofmt -r 'abs -> absFloat64' > abs_float64.go
    ...
The downside is that the API is annoyingly non-generic (a different abs variant for each type) but at least you didn't have to type it in a bunch of times.

I agree that abs() is kind of a toy example, but this approach has helped me a lot for various slice operations like indexing and deleting.

Density.io 12 years ago

Your device is doing the equivalent of standing on the street corner and screaming various things.

Except there's one huge difference: anyone with eyes and ears can understand what the person is screaming. I would wager that even fewer than half of HN readers realize what's being broadcasted here.

What you're saying is that every human must have complete technological and implementation knowledge of any possible invention or they are not deserving of privacy. That position is not only elitist as a technophile, it's arrogant to think that any one person could possess that knowledge, let alone billions. At some point, you'll be the clueless one.

Tmux 1.9 released 12 years ago

mosh + tmux are simply amazing together. It has radically improved my life, where I keep persistent connections from my laptop to various remote machines.

Tmux 1.9 released 12 years ago

The one downside to tmux over screen is that tmux basically requires that all clients connecting to it have the same screen size, and by default it chooses the smallest one. So while you can connect to an existing session, the output might be ugly.

For this reason, I typically simply 'tmux detach;tmux attach"

I really, really want the regulators to stop this, but most people only have the choice of a single cable provider anyway. Where in the US do Comcast and Time Warner compete head-to-head? It feels as though they have divided up the country similarly to how the baby bells were after AT&T was broken up in 1984.

Webhooks Level Up 12 years ago

I wrote a tool to integrate GitHub pull requests with Jenkins builds called Leeroy: https://github.com/litl/leeroy

This new feature will make things so much easier to set up and debug, I'm thrilled. Before, you could not create a pull request hook without using the API, and this caused a lot of people confusion. We set up the hook automatically in Leeroy, but this will allow people to set it up manually if they want, and debug when it doesn't work.

Was the OS X binary built without cgo? I can't seem to access containers in private https registries:

    $ docker login https://registry.example.com
    2014/02/05 14:36:20 Invalid Registry endpoint: Get https://registry.example.com/v1/_ping: x509: failed to load system roots and no roots provided
The hostname in question has a valid SSL certificate. I encountered a similar problem in the past with Go built from homebrew[1][2]. Has anyone else seen this?

[1] https://github.com/Homebrew/homebrew/pull/17758 [2] https://code.google.com/p/go/issues/detail?id=4791

Update: Filed a bug against docker, others are having the same issue. https://github.com/dotcloud/docker/issues/3946