4

In Go, is it idiomatic to check for nil and return an error if a parameter is nil?

Should pointer method receivers ever include nil checks?

I've seen a lot of code in other languages where people applying defensive programming also checked for nulls and threw an exception when a null was passed in. I am wondering if the Go-equivalent, checking for nil and returning an error, is idiomatic in Go. Say some function returns nil for some reason and you pass in nil into another function. This function doesn't handle nils and somewhere down the stack inside of the function a "kaboom" occurs, leaving the caller wondering whether the error is inside of the abstraction or in his calling code.

enderland
  • 12,091
  • 4
  • 51
  • 63
sqroot
  • 49
  • 1
  • 2
  • 1
    What happens if you don't do it? I don't think "go-thonic" applies here; if you dereference a nil pointer, the same thing happens in go as in other languages; kaboom. – Robert Harvey Sep 28 '15 at 16:26
  • I've seen a lot of code in other languages where people applying defensive programming also checked for nulls and threw an exception when a null was passed in. I am wondering if the Go-equivalent, checking for nil and returning an error, is idiomatic in Go. Say some function returns nil for some reason and you pass in nil into another function. This function doesn't handle nils and somewhere down the stack inside of the function a "kaboom" occurs, leaving the caller wondering whether the error is inside of the abstraction or in his calling code. – sqroot Sep 28 '15 at 16:34
  • Sounds like sufficient reason to do it to me. – Robert Harvey Sep 28 '15 at 16:34
  • I've checked code in the standard library and most functions don't seem to handle nil. Handling nils in pointer method receivers also feels really awkward. – sqroot Sep 28 '15 at 16:38
  • Sounds like they're leaving it up to the caller to check. Whether you check or not really kinda depends on whether you want kaboom or not. – Robert Harvey Sep 28 '15 at 16:55
  • If nil input is a programming error (as it often is) then no, just let the runtime panic when/if it's dereferenced. – Dave C Sep 28 '15 at 17:12
  • Doesn't the information if something is a programming error or a user error mostly depend on the calling context? When I want an abstraction to be reused, I usually have a hard time deciding whether the caller will consider something a user error or a programming error. – sqroot Sep 28 '15 at 17:25
  • I don't know about the situation in GO, but in C++ the main reason to be lazy about `NULL` pointers is that a segfault is among the easiest errors to debug. Just start a debugger on the core dump or rerun under control of a debugger, and it will point you exactly to the point where the `NULL` pointer was dereferenced, with all information about the call stack, telling you where the `NULL` came from. Now, if you checked for `NULL` somewhere and threw an exception, the stack will be unwound and the debug information lost when the uncaught exception finally crashes your app. – cmaster - reinstate monica Sep 28 '15 at 17:48
  • @cmaster I see, so a reason for why it isn't idiomatic to apply defensive programming to nil would be pragmatism. Most (but probably not all) errors caused by nil are programming errors. Though I am wondering if the subset of nil-errors that are user errors matters. – sqroot Sep 28 '15 at 18:35
  • User errors have to be checked, of course. Just like errors during input have to be checked and handled. Likewise, when a function may return `NULL` to signal a normal condition, a check for `NULL` is usually required. The problem is, to tell the two situations (where the check is required and where it is not required) apart. That's where we programmers always fail. But at least, we can hunt down a missing `NULL` check quite easily. Hunting down an illegit `NULL` is harder if it was checked for and an error/exception was raised. Unfortunately, users seem to have different preferences... – cmaster - reinstate monica Sep 28 '15 at 19:05

1 Answers1

2

Checking for nil, and returning an error is not a pattern I have seen in Go (I have been using it professionally for about 6 months at the moment).

If a function that does not allow nil, e.g. a pointer method receiver, is called with a nil value, that is a programming error - and when an error is caused by something that is a programming error I think that the idiomatic solution is to panic which is actually what will happen when the pointer is dereferenced.

As an example in the go library, the reflect package has plenty of methods that can panic when called incorrectly. E.g. Value.Float() will panic if the Value instance does not refer to a floating point value.

error return values are, from what I can tell, not so much to communicate programming errors, but rather situations happening outside the control of your application, e.g. cannot connect to database, file cannot be written to, network connection lost, etc.

I have certainly not checked the receiver in pointer method receivers in the Go code I have written.

Pete
  • 8,916
  • 3
  • 41
  • 53
  • 4
    I haven't used Go, but from a general programming perspective, "just dereference it because it'll throw an exception anyway" is a bad idea for one specific reason: if you just dereference it, it will throw a very generic exception. If you check the parameter and have it throw an exception, you can have it create a more useful error message that gives you better information about what went wrong, which makes tracking it down and fixing it easier, such as by knowing the name of the param that was nil instead of just "something somewhere NPD'd on you, good luck finding it!" – Mason Wheeler Sep 28 '15 at 19:25
  • I don't think you can make good assumptions about whether something will be a user or a programming error when writing an abstraction, even when you are dealing with things that are inside of the control of your application. An example for this would be abstractions used in validation (An example for this is `strconv.Atoi`), where the result is also used for the calculation, possibly because validation might require a lot of duplication of effort in regards to the execution. I agree that nil-errors are mostly (if not always) programming errors, though. – sqroot Sep 28 '15 at 20:01
  • @MasonWheeler "and have it throw an exception" Go doesn't have exceptions. For a programming error a panic is preferable to an error return. If the return is unchecked it's silently ignored, if checked the programmer has to arrange to dump/print sufficient info to find the error... whereas panic dumps out a nice stack trace and a summary of all running goroutines, etc. Makes it real easy to find the place making the incorrect call. – Dave C Sep 29 '15 at 09:42
  • 1
    @DaveC: [Unless you `recover` it,](https://github.com/golang/go/wiki/PanicAndRecover) which makes it exactly equivalent to an exception AFAICT. – Mason Wheeler Sep 29 '15 at 09:51
  • @sqroot don't make assumptions for things you don't know. If you don't know let it panic. E.g. [bytes's `Buffer.Truncatate`](https://golang.org/pkg/bytes/#Buffer.Truncate) says "It panics if n is negative or greater than the length of the buffer". If for some reason a caller is passing a raw user provided integer value directly to that function it is up to the caller to check for legal values if they want to return an error instead of letting it panic. – Dave C Sep 29 '15 at 09:51
  • 1
    @MasonWheeler, In Go panic+recover are not at all meant for regular control flow like "true" exceptions in other languages. There are rare cases where using them internal to a package as such make sense (such as the `encoding/json` package) but that is rare. – Dave C Sep 29 '15 at 09:54
  • @DaveC Presuming that you want to panic on programming error and return an error on user error, the case you provided isn't that simple, atleast in my opinion. When writing "Truncate" you don't know if "n" is coming from a user (from a config for instance) or from a programmer. I think I've come up with a good rule of thumb: If the caller could possibly easily check the parameter for correctness and the parameter will probably not be directly associated with the user (although you can never be sure ;)), then a panic is fine. – sqroot Sep 29 '15 at 16:34