Live data from Hacker News

Go subtleties

harrisoncramer.me

161–170 of 190 posts

Re: Go subtleties

#161
post #19

As somebody who only views Go from a distance, I see this list as a combination of „what‘s the big deal?“ and „please don‘t“.

I'm amused by posts like this because it shows that Go is finally slowly moving away from being an unergonomically simplistic language (its original USP?) to adopt features a modern language should have had all along. My experience developing in it always gave me the impression that the designers of the language looked at C and thought "all this is missing is garbage collection and then we'll have the perfect languag…

C at least has const ptr. In go I've seen pointers mutated 7 levels down the callstack. And of course, the rest of the sphagetti depended on those side effects.

C is so limited that you would try to avoid mutation and even complex datastructures.

Go is "powerful" enough to let you shoot yourself much harder.

Go with `const` and NonNull (call it a reference if you need) would be a much nicer language

Re: Go subtleties

#162

Earlier quoted context omitted.

Wow how did I not know of this?! How does it cancel in-progress goroutines when the provided context is cancelled?

They have to all use the special context.

They just need to be context aware, or call context-aware things.

Re: Go subtleties

#163
post #84

FTA: > Runes correspond to code points in Go, which are between 1 and 4 bytes long. That's the dumbest thing I've read in this month. Why did they use the wrong word, sowing confusion¹, when any other programming language and the Unicode standard uses the correct expression "code point"? ¹ https://codepoints.net/runic already exists

Seeing as two of the authors designed utf8 (or at least concurrent to others), I think it’s safe to defer to their expertise and nomenclature here.

Re: Go subtleties

#164
post #152
post #140

Earlier quoted context omitted.

Formal proof languages are pretty neat, but nobody really uses them in the real world. Among the languages that people actually use on a normal basis, even those that claim to have extensive type systems, they still rely on testing for most everything, and once you're relying on testing anyway the type system isn't any kind of real saviour. There is, perhaps, some segment of the developer community who believe that t…

Typescript’s type system is a huge leap up from JavaScript. You still need tests for functionality (this function does what it should) but the type system removes many error cases automatically.

It is a huge boost in developer ergonomics.

But doesn't change the tests you need to write, and those tests are going to incidentally cover anything the type system is also going to catch, so the type system isn't going to somehow make your software more reliable.

A much more expressive type system can get you there, but you won't find that in any language anyone actually uses on a normal basis.

Re: Go subtleties

#165
post #91

my favorite go trick is a simple semaphore using make(chan struct{}, CONCURRENCY) to throttle REST api calls and other concurrent goroutines. It’s really elegant acquisition by reading, and releasing the semaphore by writing. Great to limit your rest / http crawlers to 8 concurrent calls like a web browser.

Why not use the standard-library adjacent semaphore package?

One problem with using a channel as a semaphore is you need to track if you've closed the channel when "releasing".

https://pkg.go.dev/golang.org/x/sync/semaphore#Weighted.Acqu...

Re: Go subtleties

#166
post #151
post #126

Earlier quoted context omitted.

Use sync.Map or create simple wrapper to control access.

… the documentation for sync.Map literally says: > The Map type is specialized. Most code should use a plain Go map instead, with separate locking or coordination, for better type safety and to make it easier to maintain other invariants along with the map content. The documentation basically says that it's optimized for some cases that wouldn't affect the complaint above.

Well, sync.Map is based on atomics which comes with limitations on its own as you have to use specific (potentially conflicting) hash keys for it.

What I wanted to point towards with my earlier comment is that sync.Map doesn't use resource based mutexes, it uses one mutex for everything which will always be the slowest case.

There's no real borrowing concept in Go, as there would be in Rust for that case, and if you argue that simplicity should be preferred then normal map[] should be threadsafe by default, which, in return, likely will require compile time expansion because of generics.

The core value of Go's "we want simplicity" approach always feels like it's a facade, because there is so many exceptions and footguns along the way. These footguns almost always were conscious decisions where they could have made a decision to break legacy behavior for better simplicity but decided that this is not what they want, which is weird as it's not fitting the initial language design promise.

Re: Go subtleties

#167
post #110

Earlier quoted context omitted.

Because in the last dozen times I've handled this question the root cause is lack of understanding of why. Inductively it is logical to conclude that's the reason next time. It is probably also the case the bulk of readers of this conversation are still in the camp that don't understand the problem correctly. If you understand that there isn't really a fix and just wish there was one anyhow, while I still disagree in…

I can't think of good way to give programmers control over boxing without adding a bunch of complexity that nobody wants.. but it doesn't seem out of the realm of possibility that the linter could detect issues like this. It should be able to spot methods that aren't nil-safe and spot nil values of those types ending up in interfaces with those methods. Then you'd have less explaining to do!

It's a bit difficult statically because it is a flow sensitive analysis.

You are not wrong that it is a sharp edge. Completely removing nils from interfaces is not possible because: 1. not backward compatible

However I would nuance a little. Having an empty interface ie. a untyped nil is useful. Having typed nils in interfaces is arguable. Because every value type that has methods can make pointer. That means potential deref if any such pointer is passed to an interface variable instead of the value itself.

Being able to keep nil from some interfaces would be useful.

You're not wrong. In general there is not much value in having working methods on a typed nil pointer.

If we think in terms of bottom wrt type theory, yes it is supposed to implement every type. But that would be closer to untyped nil and that's not how go's type system works either. It is close though. We just don't have a language concept for nillable int because variables are auto initialized to 0. And because it would be difficult to encode such an information purely virtually. But that could be possible in theory, without mechanical sympathy. I digress. The takeaway is that I don't think a linter can do the trick easily but there has been good attempts. And it is worth pondering, you're right.

Re: Go subtleties

#168
post #102

The wording "Subtleties" used here is some weird/improper. I see nothing subtle here. They are all basic knowledge a qualified Go programmer should know about. They are many real subtleties in Go, which even many professional Go programmers are not aware of. Here are some of them: https://go101.org/blog/2025-10-22-some-real-go-subtleties.ht...

The examples in your link don’t seem to be very useful compared to the subject of this post.

“for true {...} and for {...} are not eqivalent”

So what? The compiler will tell you the first time you try to run that “for true” abomination that it is invalid code.

Re: Go subtleties

#169
post #165
post #91

my favorite go trick is a simple semaphore using make(chan struct{}, CONCURRENCY) to throttle REST api calls and other concurrent goroutines. It’s really elegant acquisition by reading, and releasing the semaphore by writing. Great to limit your rest / http crawlers to 8 concurrent calls like a web browser.

Why not use the standard-library adjacent semaphore package? One problem with using a channel as a semaphore is you need to track if you've closed the channel when "releasing". https://pkg.go.dev/golang.org/x/sync/semaphore#Weighted.Acqu...

I don’t like libs

Re: Go subtleties

#170
post #4

Earlier quoted context omitted.

I ditched Go after an evaluation years ago. I can remember it was an issue with nil pointers being non-intuitive that turned me off. And exception handling. A pity because the runtime and ecosystem/community seemed pretty good.

> And exception handling If you read & write Go regularly, the rather verbose error handling simply fades into the background. That said, errors in Go don't really translate to Exceptions as generally thought of; panic , however; may be does. Making changes to error handling wasn't for the lack of trying, though: https://news.ycombinator.com/item?id=44171677 > issue with nil pointers This is why most APIs strive for…

I read & write Go regularly, and I hate its error handling intensely
Post reply on HN