Back to News
Advertisement
Advertisement

⚡ Community Insights

Discussion Sentiment

45% Positive

Analyzed from 1035 words in the discussion.

Trending Topics

#nil#null#language#pointer#checks#never#type#need#check#references

Discussion (23 Comments)Read Original on HackerNews

diarrheaabout 2 hours ago
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<&T>` in Rust.
Groxxabout 2 hours ago
Don't forget mutability! Go throws that on top too.
Animatsabout 1 hour ago
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.
maccard11 minutes ago
Im not entirely sure this helps with your point but;

The contract is that the reference is still non-null, and that the error is dereferencing the pointer. There’s two big problems with defining the behaviour of the deterrence - 0 is a valid memory address on some (ancient) platforms so for better or worse the behaviour is platform dependent.

The other is that there’s many other ways to have absolute garbage in a pointer that aren’t null.

    int& foo() { 
        int local = 42;
        return local;
    }
Now, a compiler catches this case, but the point is that null isn’t the only invalid state that needs to be checked. Adding a compiler overhead of checking each pointer to every single pointer dereference wouldn’t work.

Modern codebases ran with static analysis tools will catch these errors (honestly even valgrind will find most if not all of these).

FartyMcFarter27 minutes ago
> 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.

PoignardAzurabout 1 hour ago
For the longest time I thought this line would lead to a crash just because it seemed so obvious. So close indeed.
poly2it11 minutes ago
It's really difficult to view Go as a serious language when fundamental design decisions such as this one have seemingly been glossed over. It's in a precarious spot, on the one hand cushioning the C it wants to resemble, but on the other hand not yielding any capable tools or abstractions which could otherwise be unlocked via the safe architecture. Go developers seem uninterested in language design.
throwa356262about 2 hours ago
What the article said applies to Rust ref vs ref-option too.
tialaramex5 minutes ago
Not really. It's possible to write this mistake but it's pretty obviously a bad idea, I've never seen someone do this and need correcting.
Fire-Dragon-DoLabout 1 hour ago
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

mirekrusin2 minutes ago
This suggestion fails for values that can be null, need to be mutable or need references from multiple places etc.

Go has a problem, "just remember to always do X, never Y" patterns can't be guaranteed across all libraries you use, can't be enforced, can be violated for good reasons and as mistake etc etc.

Shame because otherwise it's a great language, but some mistakes are just no-go.

So close indeed.

galkkabout 1 hour ago
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 ).

usrnmabout 2 hours ago
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.
Sharlin25 minutes ago
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.
bediger40003 days ago
This is good advice for humans: they can quantify to decide "too many nil checks" or not. But it's not good for agentic coding, which we're entering the age of. Although agents are the worst they'll ever be right now, they're never going to be great at quantifying too many nil checks. I think we'll have to get used to far more nil checks than even bad programmers put in. But that doesn't matter to agents, they've got infinite attention spans, no cognitive bias and large working memories. Sonn we'll see no nil checks.
lenkite1 day ago
Delegate all `nil` and bad input checks to a validation framework and use it in all your constructor functions.
FridgeSealabout 1 hour ago
I’ll go you one better: integrate it into your language and have the compiler enforce it for you!
turtleyacht3 days ago
What about wrapping nil in a Maybe or Option type?
flowerthoughtsabout 2 hours ago
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...

ThePhysicist42 minutes ago
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.

cubefoxabout 2 hours ago
Or a set theoretic type system with union type declarations (foo|null), like in TypeScript.
sail0rm00nabout 2 hours ago
References like C++, maybe?
aarjaneiroabout 2 hours ago
This is more about a hard dependency which causes a function to early exit