logoalt Hacker News

phire01/22/20251 replyview on HN

Ok... the only thing that go version directives do is selectively enable new features. Essentially, they are only really there to help you ensure your code will continue compiling in older versions of the compiler. Hell, until recently it wouldn't even throw an error if you tried to compile code with a future version directive.

The actual backwards compatibility in go is achieved by never removing functionality or syntax. New versions can only ever add new features/syntax. If there was a broken function in the API, that function needs to stick around forever, and they will be forced to add a second version of that function that now does the correct thing.

So, you can take code written for go 1.0, slap on a "go 1.23" directive and it will compile just fine. That's the guarantee that go provides. Well, mostly. There are a few examples of go 1.0 code that doesn't compile anymore, even when you use a "go 1.0" directive.

But not being able to remove anything ever is limiting.

A good example of how this can be limiting is reserved keywords. Go has a fixed set of reserved keywords that they picked for 1.0, and they can never reserve any more, any code using them as identifiers will break. Any new feature needs to be carefully designed to never need a new reserved keyword. Either they reuse an existing keyword (which c++ does all the time), or they use symbols instead.

But rust can reserve new keywords. The 2018 edition of rust reserved "async" and "await" for future async functionality and "try" for a potential try block. Rust did reserve "yield" from the start for generators, but decided they needed a way to mark a function as a generator, so in the 2024 edition, "gen" is now a reserved keyword, breaking any code that uses gen as a function/variable name.

Do note that rust also follows the go strategy within an edition. There is only one new edition every three years, and it would be a pain if all new features had to wait for a new edition. So the async/await keywords were reserved in the 2018 edition, but didn't actually get used until the end of 2019.

This means that just because your rust version supports the 2018 edition, doesn't mean it will compile all 2018 code. The editions are just for breaking changes, and there is a seperate minimum "rust-version" field that's somewhat equivalent to go's "go 1.x" directive. Though, "rust-version" doesn't disable features, it's just there to provide a nice clean warning to users on old compilers. Ideally in the future it will gain the ability to selectively disable language features (as rust already has extensive support for selectively enabling experimental language features in nightly builds, which we haven't even talked about here).

Basically, rust editions allow all breaking changes to be bundled up and applied once every three years. They also provides a way for compilers to continue supporting all previous editions, so old code will continue to work by picking an old version. While go's version directive looks superficially similar to editions, it is there for a different reason and doesn't actually allow for breaking changes.


Replies

9rx01/22/2025

> The actual backwards compatibility in go is achieved by never removing functionality or syntax.

The previous version removed functionality related to loop variables, so that is not strictly true. You might be right that the project doesn't take change lightly. There has to be a very compelling reason to justify such change, and why would it be any other way? If something isn't great, but still gets the job done, there is no reason to burden developers with having to learn a new language.

Go is not exactly the most in-depth language ever conceived. There is not much functionality or syntax that could be removed without leaving it inoperable. But there is no technical reason why it couldn't. The mechanics to allow it are already there, and it doesn't even violate the Go 1 guarantee to do so under the operation of those mechanics.

So, sure, it is fair to say that there is a social reason for Go making as few breaking/incompatible changes as is possible, but we were talking about the technology around allowing breaking/incompatible changes to co-exist and how the same concept could have been applied to Python. In Rust community fashion, I am not sure you have have improved on the horrible communication. We recognized that there is some difference in implementation details right from the onset, but the overall concept still seems to be the same in both cases to me.

Again, we need it dumbed down for the every day average person. Your audience doesn't eat monads for breakfast like your expression seems to believe.

show 1 reply