Live data from Hacker News

Excessive nil pointer checks in Go

konradreiche.com

11–20 of 116 posts

Re: Excessive nil pointer checks in Go

#11
post #9

This is the mess a language lands on when it conflates optionality (a semantic concept) with references/pointers (purely a machine concept). In Go, the requirement " need (non-optional) a reference to an object" is simply not expressible. This is a solved problem in other languages, for example `&T` vs. `Option ` in Rust.

What the article said applies to Rust ref vs ref-option too.

Re: Excessive nil pointer checks in Go

#12
post #9

This is the mess a language lands on when it conflates optionality (a semantic concept) with references/pointers (purely a machine concept). In Go, the requirement " need (non-optional) a reference to an object" is simply not expressible. This is a solved problem in other languages, for example `&T` vs. `Option ` in Rust.

Don't forget mutability! Go throws that on top too.

Re: Excessive nil pointer checks in Go

#14
post #9

This is the mess a language lands on when it conflates optionality (a semantic concept) with references/pointers (purely a machine concept). In Go, the requirement " need (non-optional) a reference to an object" is simply not expressible. This is a solved problem in other languages, for example `&T` vs. `Option ` in Rust.

In C++, that distinction supposedly exists. References should never be null, while pointers can be. But there's no enforcement.

    int& ref = *ptr;
ought to generate a panic for a null pointer. But it doesn't. They were so close to getting it right.

Re: Excessive nil pointer checks in Go

#15
I could have forgiven nil checks, but nil checks on interfaces elevated nils to a whole new level, which is annoying, but I do get where they were going with this: you should never nil check an interface. After all,an interface could be valid for a nil value.

There are ways to decently write go and not deal with nil, but as usual, linters defaults makes it impossible and you have to fight with your team before they will understand (we did this at some point and it was a huge improvement).

Don't use pointers at all, always allocate structs on the stack, pass them by value.

You pay the copy price, even with large structs, and that's fine. When there are exceptions, be very explicit about the reason: performance must be critical,not just an optimization.

Don't ever check interfaces for nil, if you need some sort of optional parameter, make a separate function and make it pass an valid object for that interface that's a null object.

These two did improve things substantially

Re: Excessive nil pointer checks in Go

#16
I agree with first point “Nil Check on a Dependency” and disagree with 2nd point

“Nil Check on a Dependency in the Constructor”, at least in the way it is described in article’s example.

The _parameter_ check in the constructor is the standard practice of testing on perimeter/blundaries. You test your parameters on the public methods (that constructor obviously is), and assume valid state in private methods. And even there I can accept practice of debug build assertions (DCHECK/TCHECK in Google c++ terminology ).

Re: Excessive nil pointer checks in Go

#17
post #14
post #9

This is the mess a language lands on when it conflates optionality (a semantic concept) with references/pointers (purely a machine concept). In Go, the requirement " need (non-optional) a reference to an object" is simply not expressible. This is a solved problem in other languages, for example `&T` vs. `Option ` in Rust.

In C++, that distinction supposedly exists. References should never be null, while pointers can be. But there's no enforcement. int& ref = *ptr; ought to generate a panic for a null pointer. But it doesn't. They were so close to getting it right.

For the longest time I thought this line would lead to a crash just because it seemed so obvious. So close indeed.

Re: Excessive nil pointer checks in Go

#18

What about wrapping nil in a Maybe or Option type?

In this case, the missing piece in Go is the NonNullable hint. That would make it clear that null checks aren't needed, enforceable by the type system, and lintable. Option types just forces you to do the check, but doesn't remove the need for it. Now that we have generic types, a NonNullable intrinsic type seems doable...

It's quite easy to write a generic Maybe struct that performs most of the encapsulation that Rust's Maybe does i.e. allow unwrapping of the inner type through a function or handling the nil case through a switch like statement. I've never seen this in the wild which makes me think people don't care about it too much. And of course it's runtime based so no compile time guarantees, and just to preempt the expected replies I know it's not the same what Rust is capable off and Rust is of course a much much much much better language than Go.

Personally I do experiment with these things as it makes code more readable, it just seems adoption for generics and what you can do with them is still quite low in the broader community. That said I do not deal with null pointer exceptions much at all, and when I do it's often relatively simply to spot and fix, so for me it's not a large issue.

Re: Excessive nil pointer checks in Go

#19
post #14
post #9

This is the mess a language lands on when it conflates optionality (a semantic concept) with references/pointers (purely a machine concept). In Go, the requirement " need (non-optional) a reference to an object" is simply not expressible. This is a solved problem in other languages, for example `&T` vs. `Option ` in Rust.

In C++, that distinction supposedly exists. References should never be null, while pointers can be. But there's no enforcement. int& ref = *ptr; ought to generate a panic for a null pointer. But it doesn't. They were so close to getting it right.

> They were so close to getting it right.

The philosophy of C++ is to not introduce unnecessary overhead, and to trust the programmer. This design choice is prevalent throughout the language. They were never going to make an exception, especially for something as prevalently used as references.

There are countless examples of this "no unnecessary overhead and/or trust the programmer" choice:

- primitive types and standard containers are not thread safe - it's up to the programmer to know this and use them accordingly.

- std::unique_ptr lets you grab the underlying raw pointer, in which case it's no longer a "unique_ptr". But there are cases in which it's useful to do this (e.g. interfacing with C code), so they let you do it, and trust that you do it in a safe way. They could have made unique_ptr not support this, but then it would be less useful (or force you into copying data unnecessarily to call an API that requires a raw pointer).

> But there's no enforcement.

There's no strict enforcement, but it is undefined behaviour, so compilers can randomly choose to act as if it's enforced and simply crash your program or make it act weirdly.

Re: Excessive nil pointer checks in Go

#20
post #8

Also known as contract programming vs. defensive programming. This argument is very old, is not specific to golang, and I have found myself on both sides at different points in my carreer.

Fortunately we have type systems to encode many contracts at compile time, including stuff like optionality. Certainly no modern language would still repeat Hoare’s "billion dollar mistake"? Right? …Oh.
Post reply on HN