My personal hypothesis is that many libraries follow the official Go release policy[1]. Due to the fact that generics were introduced with v1.18 and we now have v1.19, libraries would be bound to the features that were available with v1.17, unless they spend effort on working around this by implementing the respective build flags that make generics available exclusively for >=v1.18. However this is just an assumption.
According to the Go Developer Survey 2022 Q2[2], 1 in 4 respondents said they've already started using generics in their Go code and 14% have started using generics in production code.
[1] https://go.dev/doc/devel/release [2] https://go.dev/blog/survey2022-q2-results
That said, libraries often want to be friendly to older Go versions, so I agree with your overall hypothesis.
Backwards compatibility is another major concern for libraries: I assume you can't safely change the signatures of existing functions from interfaces to generics, for example. (Well, aside from some special cases like interface{} becoming "any".)
Also also: in quite a few cases switching to generics is a backwards-incompatible change or an "ugly" change (similar to Foo() and FooContext()), which makes adding them to existing libraries a bit painful.
So until the learning material catches up and until new libraries are written, or old ones have a reason to be updated, you won't see generics widely used.
Where Go and C# have differed is where generics are common. In the introduction of generics into C#/.NET, their use was added everywhere. Microsoft made a huge point of including built-in List<T> and ArrayList<T> and other common things that would benefit from generics.
Go however has drawn the line in the sand that generics are not allowed into the standard library for now. Rob Pike has been vocal about this [1] and I can almost agree with him.
Part of the issue is that the way Go handles the standard library. Part of the issue is how libraries in general are managed.
* Go’s standard library is simultaneously “just a git repo in the ether” and “A thing shipped with the Go compiler runtime”. Like any other library. * Libraries in general being “Just another git repo”, many haven’t been updated to use Go modules. Because Go doesn’t have a good central repository for go libraries, I don’t have good numbers on this.
For instance, we have an internal library that helps us create and perform common operations on concurrency-safe maps. Being able to pass and return arbitrary structs to/from those functions has made our codebase much cleaner and easier to work with.
Please take my response with a grain of salt, as we have not benchmarked or otherwise compared (via escape analysis, etc.) those generic functions against any non-generic equivalents.
Most people (who use go for network IO bound services) will not notice.
One other side effect, now that Go has generics, we can all stop talking about them. Use them or don't. :)
It's a very frameworky piece of code, which is exactly the kind of thing that can benefit from generics.
https://www.scylladb.com/2022/04/27/shaving-40-off-googles-b...
The only time I have used them in the last year was building a generic LRU memory cache and it worked wonderfully.
Generics are typically very useful in a few very specific contexts but in most applications you can get by fine without them.
Library authors want to support old go versions, and the library usage of Generics might not work on that older version.
I just started using generics because I wanted a couple release cycles for issues to be shaken out. I would suggest to most library authors to wait until 1.20 is released before using generics, and if you are doing anything slightly sophisticated to make sure you have a CI that builds with older versions of Go.
If you are writing application code, then go ahead and use generics, but generics are usually most useful for libraries.
I was recently able to fork a library for better error handling in Go and make a version similar to the try proposal. This library wouldn't have been possible without generics: https://github.com/gregwebs/try
There are many experienced developers who (through long experience fighting C++) have come to understand that using a simple language (like Go or C) allows you focus on the algorithms and complexity of the problem you're trying to solve. This is part of the Go culture.
A junior developer might not understand this yet. He's still having fun finding obscure and convoluted contortions of the language that allow him to save little or no effort... not yet realizing that nobody gives a shit and he's wasting his time and the time of his coworkers.
There's a major issue of programmers trying to impress each other, by creating complex convoluted monstrosities, and not realizing what is actually important. It takes them awhile to find out that what they have produced was not impressive, and the people that know better, are also not impressed.
E.g. a simple map is not implementable in a sane manner in a static language without generics, because you likely want to know the type of the object you get from there at compile time. There is a reason C has no sane data structures at all, everyone has their own creation which either brings in a big deal of overhead, or employs copy-pasting mostly the same code (which is pretty much what generics do behind the scenes, at least in an overly simplistic manner).
I still haven't learned generics.
In particular I looked at places where we use code generation and places where we use reflection - both good candidates for generics.
Alas I found generics just weren't powerful enough to replace these uses, it would need a more powerful macro system.
I think go generics are great for container libraries. I've written and debugged enough of those to be very happy using a library.
I doubt Go will ever get macros though.
I really like Zig's comptime mechanism - that seems like the right amount of power and it's all just zig code!
Mostly though, most of the stuff people were complaining about regarding Go's lack of generics were really minor things, like map/filter versus for loops. There are some things you really couldn't do easily in Go without generics, but for the most part Go's critics were just obstinate about not wanting to learn a new style of programming (so much so that many are willing to forsake all of Go's considerable advantages with respect to tooling, deployment model, build times, package management, ecosystem, etc). Moreover, Go's critics were (and still are) unwilling to admit there are benefits to avoiding generics--the code becomes less abstract and more standard as there are fewer ways to express a given solution.
If you really want to dunk on Go, the more valid criticism is that it lacks sum types, and unlike converting a map/filter chain to a for loop, there is no good way to emulate sum types in Go (there are some tricks to get close, but they involve far more boilerplate and they don't give exhaustiveness checks (and the performance tradeoffs suck).
type OptionInt enum { Int(int), None }
Yeah this means you would have to write that line for every type, but that’s pretty in-line with Go’s philosophy that a little bit of copying is better than a little dependency. But the people who optimize for keystrokes above all else will take issue with it.Go is the cloud native language, there is no need to change your production code only just to add the latest shiny feature, that goes against the philosophy of Go
people will adopt the new features slowly but surely as they get battle tested
have a function take in an array of T
T is generic with type constraint call Constraint
problem is calling that function wont work because it has to instantiated with one of the types found in the Constraint which beats the whole purpose. I need this to pass in structs that belong to Constraint.
I tried a multitude of different things and nothing seemed to work.
I ended up reading the Go library for how it handles SPrintF args and did something similar with a type switch and reflection wizardry.
Im still a junior programmer so it could be that I just didnt know how or I underestimate the solution for this.
But I figured once we have a type Constraint defined with a function, it should be relatively simple to not have to instantiate it with ONE concrete type and allow the Constraint types as args.
if anyone knows a better way to do this please let me know
Hopefully someone can bridge that gap for you.
https://goplay.tools/snippet/Vj7uVMi0mWn
Basically have two different types that implement an interface, pass them into a receiver function in a composite type (array,slice).
Part of the issue is that you cant have the receiver function without defining instantiating the type on the receiver Spotter in this case.
If that's correct, then is this what you need? https://go.dev/play/p/_e-Uipgo5OQ It seems quite straightforward so I think I'm missing some context, but this can be a start.
I made an example of what Im trying to do. One thing I didnt specify is the function used is a receiver function. For some reason this means the type had to be instantiated on the receiver itself Spotter in this case. https://goplay.tools/snippet/Vj7uVMi0mWn Basically have two different types that implement an interface, pass them into a receiver function in a composite type (array,slice). Part of the issue is that you cant have the receiver function without defining instantiating the type on the receiver Spotter in this case.
https://go.googlesource.com/proposal/+/refs/heads/master/des...
I believe the most simple fix would be to have a separate function which is generic that takes in the struct along with the slice of T with the constraints that apply to it.
So was not having generics a deal-breaker? No! Are they useful? Yes!