joeshaw
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.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.
`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 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.
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.
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.
> 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
The Novell-owned IP was either sold or licensed to Xamarin later.
Most of them seem to be interfaces to network switches.
We clearly need to update the copyright info on the page and make some new blog posts!
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.
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.
Twitter's incorporation of the POI will give them much more "check-in" data than their own apps ever could.
https://medium.com/@dens/six-years-in-a-few-thoughts-on-four...
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.
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.
Details on the protocol are here: https://docs.google.com/document/d/1ABI0kqUUxoAjxhWW3AsWFis6...
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.
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.
I am the CADT; and advice on NEEDINFOing old bugs en masse
http://jasonwilder.com/blog/2014/03/25/automated-nginx-rever...
Automated Nginx Reverse Proxy for Docker
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.
For this reason, I typically simply 'tmux detach;tmux attach"