Live data from Hacker News

Excessive nil pointer checks in Go

konradreiche.com

101–110 of 116 posts

Re: Excessive nil pointer checks in Go

#101
post #80
post #78

Earlier quoted context omitted.

Yes it is. 1) Most of pointers in real apps are non-nullable and it's nice to have enforcement from a compiler. 2) Good compilers verify you actually check nullable (optional) values have a corresponding check. In particular Zig literally forces you to unwrap value, so no unexpected state. It's a really amazing QoL. And no, optionality doesn't make type system any harder. Also C lacks alignment enforcement on type le…

My worries about coding all these things into type systems is that this freezes the semantics at a time during development where one is still figuring out the ideal semantics. In any case, I wonder what you think about my experimental maybe type? https://godbolt.org/z/MTdj81841

> My worries about coding all these things into type systems is that this freezes the semantics at a time during development where one is still figuring out the ideal semantics.

I don't get this part at all, especially in a language with static typing. It's completely normal for me to not be sure which type I need here and so I use a type alias, or if I'm even less sure I make a wrapper type so that it only has the properties I choose. So I don't see this "freezing" semantics any more than an identifier name would - we can just change it if we change our minds, no problem.

To give a comparable example from elsewhere, when I'm still feeling out the problem I often write Rust's loop, the infinite loop which is Rust's only primitive looping structure (for and so are just sugar). That doesn't mean I write only weird odd-ball loops, but when I'm not sure the raw infinite loop is fine if I decided to consume all the Widgets in this Vec like a for-loop but it's also OK if I decided to index through it, or do something entirely different. Rust's "clippy" will prompt me to write a more idiomatic while-let or for-each loop when I've nailed down the whole thing.

Re: Excessive nil pointer checks in Go

#102
post #56

Earlier quoted context omitted.

This is the most boring argument in computer science. It's like arguing about whether a language should have "goto" or not. There is no new ground to tread here. Most mainstream languages have null references. An entire cinematic universe of languages have been built from the premise that you should not have null references. This is a fundamental rift in programming language theory, and the very best you can do on HN…

> It's like arguing about whether a language should have "goto" or not We all agreed they shouldn't, right? I'm not familiar with any language designed in the last 15 (20?) years that has a goto statement. So, yes, I agree this is a very old argument and it's sad that Golang repeated the failures of its predecessors. At least it doesn't include "goto", though the temptation to do it for the wordplay must have been in…

No, nobody agrees about these things, which is why this is the most boring argument in computer science. You can't even get people to agree on typing. Not "which type system", any of it.

If you think your current position on these debates --- nullability, gotos, typing --- is the obviously correct position, you simply haven't talked to enough people. The answer to all these questions --- the real answer --- is "it's more complicated than you think".

Re: Excessive nil pointer checks in Go

#103
post #56

Earlier quoted context omitted.

This is the most boring argument in computer science. It's like arguing about whether a language should have "goto" or not. There is no new ground to tread here. Most mainstream languages have null references. An entire cinematic universe of languages have been built from the premise that you should not have null references. This is a fundamental rift in programming language theory, and the very best you can do on HN…

> It's like arguing about whether a language should have "goto" or not We all agreed they shouldn't, right? I'm not familiar with any language designed in the last 15 (20?) years that has a goto statement. So, yes, I agree this is a very old argument and it's sad that Golang repeated the failures of its predecessors. At least it doesn't include "goto", though the temptation to do it for the wordplay must have been in…

> At least it doesn't include "goto", though the temptation to do it for the wordplay must have been intense.

https://go.dev/ref/spec#Goto_statements

Re: Excessive nil pointer checks in Go

#104
post #42

Earlier quoted context omitted.

it does not resolve the problem. you would need to check "is this value optional?" and unpacking everywhere. this is what this article saying. you can do unpacking/nil-checks at the root or later when it happened. with rust you have 2x more ways to shoot yourself in the foot.

> with rust you have 2x more ways to shoot yourself in the foot. The checking isn't how you shoot yourself in the foot, it's the absence of checking. Rust doesn't allow you to forget to check. This entire class of problems just disappears in Rust. In this if the code needs a non-null redis client to work you take `RedisClient` not `Option `.

[deleted]

Re: Excessive nil pointer checks in Go

#105
post #42

Earlier quoted context omitted.

it does not resolve the problem. you would need to check "is this value optional?" and unpacking everywhere. this is what this article saying. you can do unpacking/nil-checks at the root or later when it happened. with rust you have 2x more ways to shoot yourself in the foot.

> with rust you have 2x more ways to shoot yourself in the foot. The checking isn't how you shoot yourself in the foot, it's the absence of checking. Rust doesn't allow you to forget to check. This entire class of problems just disappears in Rust. In this if the code needs a non-null redis client to work you take `RedisClient` not `Option `.

yes that is correct. tbh in Go for service structs that what you would do as well. use value receivers for such things. and inject dependencies as interfaces. so pointers not immediately visible and it is just type RedisClient interface in your field/arg.

Re: Excessive nil pointer checks in Go

#106
post #65

Earlier quoted context omitted.

This suggestion fails for values that can be null, need to be mutable or need references from multiple places etc – it's not "just performance penalty". 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, other patterns and as a mistake etc etc. Shame because otherwise it's a great language, but some…

> They need Go 2 with T and ? T - that would be nice language to use. Zig has basically been this to me. As a developer, writing Zig feels a lot like writing Go, except with basically all of the pain points addressed. - Zig has different pointer types for different things. The default pointer type points to exactly one value. It also has an optional pointer type as described. Arrays and slices are also pointers of so…

Honest question, what is the zig version of goroutines these days? They removed async from the spec.

Honestly the only reason why I stick to golang for some projects are goroutines.

Re: Excessive nil pointer checks in Go

#107
post #106
post #65

Earlier quoted context omitted.

> They need Go 2 with T and ? T - that would be nice language to use. Zig has basically been this to me. As a developer, writing Zig feels a lot like writing Go, except with basically all of the pain points addressed. - Zig has different pointer types for different things. The default pointer type points to exactly one value. It also has an optional pointer type as described. Arrays and slices are also pointers of so…

Honest question, what is the zig version of goroutines these days? They removed async from the spec. Honestly the only reason why I stick to golang for some projects are goroutines.

Zig 0.16.0 was the big, recent async update. Go channels are handled as std.Io.Queue, the select keyword is handled with std.Io.Select, and goroutines are handled with std.Io.async and std.Io.concurrent. The difference between async and concurrent calls are essentially: async calls may be called concurrently, or they may not (the execution of the async function is not dependent on the caller continuing to execute - like reading a set of files), and concurrent must execute concurrently (the execution of the concurrent function depends on the caller continuing to execute - like a client+server interaction, and will crash if the environment cannot spawn concurrent processes).

The fully userspace implementation is a bit more syntactically clunky than the concurrency primitives in Go, but it is very similar semantically.

Re: Excessive nil pointer checks in Go

#108
post #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.

I don't really consider Go a modern language anyhow. It's modern compared to C, but then again, so is Java, and it came out like three decades ago.

Re: Excessive nil pointer checks in Go

#109
post #14

Earlier quoted context omitted.

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 sta…

> The philosophy of C++ is to not introduce unnecessary overhead, and to trust the programmer.

The first part of that is reasonable. The second part is just naive; nobody writes bug-free code, so if your strategy for not having memory corruption is "just don't screw it up", you're going to get quite a lot of memory corruption, and that's how we ended up where we are today.

Re: Excessive nil pointer checks in Go

#110
post #14

Earlier quoted context omitted.

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.

There's nothing particularly special about null pointers: you can also have an invalid non-null pointer, e.g. through pointer arithmetic. When you write `int& ref = *ptr;` you are dereferencing the pointer with `*ptr` therefore you have promised that it's valid. The compiler doesn't need to do anything to validate ptr because it already has your assurance. It's really no different than if you were to write `printf("%…

> When you write `int& ref = ptr;` you are dereferencing the pointer with `ptr` therefore you have promised that it's valid.

Yes, that's exactly the problem. "I promise I didn't make a mistake when reasoning about this code" is tied for worst strategy in the world for preventing bugs, along with every other strategy that doesn't actually prevent bugs.

Post reply on HN