== Explicit error checking and the broad exception catching problem As I was writing yesterday's entry on [[a subtle over-broad _try_ in Python ../python/SubtleBroadTry]], it occurred to me that one advantage of a language with explicit error checking, such as [[Go http://golang.org/]], is that a broad exception catching problem mostly can't happen, especially accidentally. Because you check errors explicitly after every operation, it's very hard to aggregate error checks together in the way that a Python _try_ block can fall into. As an example, here's more or less idiomatic [[Go]] code for the same basic operation: .pn prewrap on > for _, u := range userlist { > fi, err := os.Stat(u.hdir) > if err != nil || !(fi.IsDir() && fi.Mode().Perm() == 0) { > fmt.Println(u.name) > } > } (Note that I haven't actually tried to run this code so it may have a Go error. It does compile, which in a statically typed language is at least a decent sign.) This does the _stat()_ of the home directory and then prints the user name if either there was an error or the homedir is not a mode 000 directory, corresponding to what happened in the two branches of the Python _try_ block. When we check for an error, we're explicitly checking the result of the _os.Stat()_ call and it alone. Wait, I just pulled a fast one. Unlike the Python version, this code's printing of the username is not checking for errors. Sure, the _fmt.Println()_ is not accidentally being caught up in the error check intended for the _os.Stat()_, but we've exchanged this for not checking the error at all, anywhere. (And this is sufficiently idiomatic Go that the usual tools like _go vet_ and _golint_ won't complain about it at all. People ignore the possibility of errors from _fmt.Print*_ functions all the time; presumably complaining about them would create too much noise for a useful checker.) This silent ignoring of errors is not intrinsic to explicit error checking in general. What enables it here is that Go, like C, allows you to quietly ignore all return values from a function if you want instead of forcing you to explicitly assign them to dummy variables. The real return values of _fmt.Println()_ are: > n, err := fmt.Println(u.name) But in my original Go code there is nothing poking us in the nose about the existence of the _err_ return value. Unless we think about it and remember that _fmt.Println()_ can fail, it's easy to overlook that we're completely ignoring an error here. (We can't do the same with _os.Stat()_ because the purpose of calling it is one of the return values, which means that we have to at least explicitly ignore the _err_ return instead of just not remembering that it's there.) (This is related to [[how exceptions force you to deal with errors ../python/ExceptionsAndCasualProgramming]], of course.) PS: I think that Go made the right pragmatic call when it allowed totally ignoring return values here. It's not completely perfect but it's better than the real alternatives, especially since there are plenty of situations where there's nothing you can do about an error anyways. === Sidebar: how you can aggregate errors in an explicit check language Languages with explicit error checks still allow you to aggregate errors together if you want to, but now you have to do it explicitly. The most common pattern is to have a function that returns an error indicator and performs multiple different operations, each of which can fail. Eg: > func oneuser(u user) error { > var err error > fi, err := os.Stat(u.hdir) > if err != nil { > return err > } > if !(fi.IsDir() && fi.Mode().Perm() == 0) { > _, err = fmt.Println(u.name) > } > return err > } If we then write code that assumes that a non-_nil_ result from _oneuser()_ means that the _os.Stat()_ has failed, we've done exactly the same error aggregation that we did in Python (and with more or less the same potential consequences).