<p>Go (was) famous for its lack of generics. However, the lack of generics in a statically typed language makes quite a few things harder. For example (in a pseudocode because I don't know Go):</p>
<pre><code>let x: List = []
x.add(1)
let y: ??? = x[0]
</code></pre>
<p>In this case, what is <code>???</code>? In ye olden Java days (i.e. before Java 5), you used to have to insert a cast to <code>int</code>. However, this problem is much more elegant if you have generics. In fact, a lot more problems are made much easier with generics. How did Go (and other statically typed languages without generics) circumvent this problem and make life easier for the coder (ironic, as Go was supposed to make coding simpler?)?</p>

<static-typing><generics><language-ergonomics>
What strategies did Go use to circumvent the lack of generics?
CC BY-SA 4.0
2023-06-07T16:08:21.290Z
I admit the question is a bit vague, I have an objective question in my head but can't exactly find a good wording for it. Feel free to suggest alternative formulations.
Seggan
CC BY-SA 4.0
2023-06-08T15:49:57.017Z
I know C has void pointers and casting, but I don't know enough about C to post about it.
bigyihsuan
<p>Go <strong>always had</strong> generics!<br />
Except they were limited to built-in types and had special syntax support.</p>
<p>Generic arrays are spelt <code>[n]a</code>, slices <code>[]a</code>, generic maps <code>map[k]v</code>, and channels <code>chan a</code>.<br />
They also are created either by special literals or by the <a href="https://go.dev/ref/spec#Making_slices_maps_and_channels" rel="noreferrer"><code>make</code> feature</a>.</p>
<p>This makes your example look like</p>
<pre class="lang-go prettyprint-override"><code>x := make([]int)
x = append(x, 1)
var y int
y = x[0]
</code></pre>
<p>Now, how to have generic user types is a question Go (and other similar languages) just didn't answer, instead opting to leaving you to your own devices.</p>

CC BY-SA 4.0
2023-06-07T23:09:33.050Z
2023-06-08T01:34:35.460Z
2023-06-08T01:34:35.460Z
Longinus
<h1>Using <code>interface{}</code> and type assertions</h1>
<p>Go allows for dynamic dispatch using <code>interface</code>s, which contain the methods needed for a type to implement said interface. As a special case, the empty interface <code>interface{}</code> specifies that a type needs to match no methods to be considered as implementing the empty interface; therefore, all types implement the empty interface.</p>
<p>Through this, pseudo-generics and heterogenous data structures (like JSON's heterogenous arrays) are possible.</p>
<p>This allows something like the following, where an <code>interface{}</code> is used to store values of different types in a slice. These types can be differentiated using a switch over the type, or with type assertions to a different type.</p>
<pre class="lang-go prettyprint-override"><code>nodes := []any{12345, &quot;hello world!&quot;, 5.678}
// note: any is a builtin alias for interface{}

for _, n := range nodes {
    switch n := n.(type) {
        case int: println(n-1) // n is an int
        case string: println(n + &quot;foobar&quot;) // n is a string
        default: println(&quot;unknown&quot;) // everything else
    }
}
</code></pre>
<p>However, with type assertions, you will get a runtime error if you are unable to explicitly cast between the two types. In the above example, <code>nodes[1].(int)</code> will panic because a <code>string</code> cannot be casted into an <code>int</code>.</p>

CC BY-SA 4.0
2023-06-08T15:10:06.173Z
2023-06-08T15:48:20.430Z
2023-06-08T15:48:20.430Z
bigyihsuan
CC BY-SA 4.0
2023-06-08T15:40:39.343Z
That looks messy
Seggan
CC BY-SA 4.0
2023-06-08T15:44:39.160Z
@Seggan because it is haha. Though, as other answers have mentioned, the builtin slice/array, map, and channel types are enough for most needs. Anything more complicated tends to be placed into structs.
bigyihsuan
<p><em>Disclaimer: I don't have much experience in Go, and this answer is written from the limited experience I have. If I've got anything wrong in this answer, please leave a comment and I'll try to correct the answer.</em></p>
<hr />
<h2>Go doesn't have a <code>list</code> type</h2>
<p>That's right. You only have arrays, which need an element type:</p>
<pre class="lang-go prettyprint-override"><code>x := []int {1, 2, 3, 4};
</code></pre>
<p>Nested arrays work the same way:</p>
<pre class="lang-go prettyprint-override"><code>x := [][]int {{1, 2}, {3, 4}, {5, 6}};
</code></pre>
<p>Then, it's obvious to the compiler what type <code>x[0]</code> is. This means you don't need to explicitly specify the type. This would be perfectly fine:</p>
<pre class="lang-go prettyprint-override"><code>y := x[0];
</code></pre>
<hr />
<h2>But what if it did?</h2>
<p>The only option that I can think of is to get the programmer to explicitly put the type in the definition, like this:</p>
<pre class="lang-go prettyprint-override"><code>var y int = x[0];
</code></pre>
<p>And then at runtime try and convert <code>x[0]</code> to an integer. If that fails, raise a runtime error.</p>
<p>This isn't great though, so it's probably better just to stick to arrays, and not lists.</p>

CC BY-SA 4.0
2023-06-07T16:26:01.960Z
2023-06-07T16:46:12.930Z
2023-06-07T16:46:12.930Z
The Thonnu
CC BY-SA 4.0
2023-06-07T16:36:57.273Z
I think this answer is side-stepping the question. Lists are an example where generics are useful, and it follows from the fact that Go has no generics that Go has no generic list type. The question is how can you have things like lists in Go, when those things would require generics in other languages; so "the standard library doesn't have them" isn't an answer. If the answer is "you *can't* have them" then it would be better to spell that out, but in that case the answer is really that Go *doesn't* circumvent the lack of generics, and that this is a major limitation in the language.
kaya3
CC BY-SA 4.0
2023-06-07T16:39:30.503Z
OTOH, if the answer is "you can't have them, but you don't need them because XYZ", then it might not be a major limitation in the language. But if XYZ is just arrays then that is still a pretty major limitation, because arrays aren't a good substitute for lists, and arrays aren't a substitute at all for many other things where generics are useful.
kaya3
<p>There are two questions lurking here: what do people do in the general case for their own types, and what did they do for basic collections? The choice in (original) Go was that these did not need to be equated to one another, and built-in types could be provided to fill the niche of what people actually needed without adding generic types.</p>
<hr />
<p>Go's slice type is written <code>[]T</code>. Slices can share memory with arrays and each other, but they don't have to be derived from an existing fixed-size array, and they can be dynamically resized. This is <a href="https://go.dev/tour/moretypes/15" rel="noreferrer">one of the examples in the Go tour</a>; using the <code>append</code> function to add more elements dynamically fills in a lot of the niche of a dynamically-sized list:</p>
<pre class="lang-go prettyprint-override"><code>var s []int
s = append(s, 0)
s = append(s, 1)
...
</code></pre>
<p>These are different values, but the core of list operations are achievable. The idea is that you would write the program accordingly, and so it won't be structured the same way as a Java program doing the same thing, but you can do all the things you need to do for what Go was made for.</p>
<p>The other core generic collection is a map or dictionary type, which Go also built in <a href="https://go.dev/blog/maps" rel="noreferrer">as <code>map[Key]Value</code></a>:</p>
<pre class="lang-go prettyprint-override"><code>var m map[string]int
m = make(map[string]int)
...
i := m[&quot;a&quot;]
</code></pre>
<p>This is an implicit hash table behind the scenes, but it's an implementation detail. There's no way to get an equivalent tree map or something like that.</p>
<p>For custom types, the usual actual practice was to template out different versions with changed inner types, or to cast when that couldn't happen. The idea at the language-design level was that this would be rare. It's hard to tell how much that idea was true in practice — did people not do it much because they didn't need to, or because it was hard? You certainly saw instances of it in real-world Go codebases, but not as often as generics in Java ones.</p>
<hr />
<p>The story was simply that programmers didn't actually need generics almost all the time, and supporting them would complicate the language and implementation too much to be worthwhile, while built-in slices and maps cover most of the real-world cases. Subsequent versions of Go did <a href="https://go.dev/blog/intro-generics" rel="noreferrer">add generics recently</a>, so the story may not have worked out as well as planned.</p>

CC BY-SA 4.0
2023-06-07T23:12:16.160Z
2023-06-07T23:12:16.160Z
Michael Homer
<p>AFAIK, most Gophers tend to replace generics with duplicating classes, merely changing the contained type and the name to reflect it. A similar approach is used in C, which has macros and therefore you can do something like this</p>
<pre class="lang-c prettyprint-override"><code>#define INSTANTIATE_LIST(T) struct LinkedList_##T { T item; LinkedList_##T *next; }

INSTANTIATE_LIST(int)

LinkedList_int head = {0, NULL};
</code></pre>
<p>Using a buildsystem you can do something similar with golang's code generation tools: <a href="https://go.dev/blog/generate" rel="noreferrer">https://go.dev/blog/generate</a></p>

CC BY-SA 4.0
2023-06-07T17:09:33.510Z
2023-06-07T17:10:12.210Z
2023-06-07T17:10:12.210Z
kouta-kun
CC BY-SA 4.0
2023-06-08T03:00:14.797Z
So, essentially, roll your own templates?
Unrelated String
CC BY-SA 4.0
2023-06-08T15:10:04.187Z
@UnrelatedString yeah, not the greatest solution but people definitely do it in C and Go.
kouta-kun
# What strategies did Go use to circumvent the lack of generics?
Posted by: username_0 on 2023-06-07
Go (was) famous for its lack of generics. However, the lack of generics in a statically typed language makes quite a few things harder. For example (in a pseudocode because I don't know Go):
let x: List = []
x.add(1)
let y: ??? = x[0]

In this case, what is ???? In ye olden Java days (i.e. before Java 5), you used to have to insert a cast to int. However, this problem is much more elegant if you have generics. In fact, a lot more problems are made much easier with generics. How did Go (and other statically typed languages without generics) circumvent this problem and make life easier for the coder (ironic, as Go was supposed to make coding simpler?)?
**Tags:** static-typing generics language-ergonomics
> **Comments:**
> **username_0**: I admit the question is a bit vague, I have an objective question in my head but can't exactly find a good wording for it. Feel free to suggest alternative formulations.  
> **username_1**: I know C has void pointers and casting, but I don't know enough about C to post about it.  
---
## 5 answers
### Answer by username_5 on 2023-06-07. Score: 9 | Accepted answer
There are two questions lurking here: what do people do in the general case for their own types, and what did they do for basic collections? The choice in (original) Go was that these did not need to be equated to one another, and built-in types could be provided to fill the niche of what people actually needed without adding generic types.

Go's slice type is written []T. Slices can share memory with arrays and each other, but they don't have to be derived from an existing fixed-size array, and they can be dynamically resized. This is one of the examples in the Go tour; using the append function to add more elements dynamically fills in a lot of the niche of a dynamically-sized list:
var s []int
s = append(s, 0)
s = append(s, 1)
...

These are different values, but the core of list operations are achievable. The idea is that you would write the program accordingly, and so it won't be structured the same way as a Java program doing the same thing, but you can do all the things you need to do for what Go was made for.
The other core generic collection is a map or dictionary type, which Go also built in as map[Key]Value:
var m map[string]int
m = make(map[string]int)
...
i := m["a"]

This is an implicit hash table behind the scenes, but it's an implementation detail. There's no way to get an equivalent tree map or something like that.
For custom types, the usual actual practice was to template out different versions with changed inner types, or to cast when that couldn't happen. The idea at the language-design level was that this would be rare. It's hard to tell how much that idea was true in practice — did people not do it much because they didn't need to, or because it was hard? You certainly saw instances of it in real-world Go codebases, but not as often as generics in Java ones.

The story was simply that programmers didn't actually need generics almost all the time, and supporting them would complicate the language and implementation too much to be worthwhile, while built-in slices and maps cover most of the real-world cases. Subsequent versions of Go did add generics recently, so the story may not have worked out as well as planned.
---
### Answer by username_6 on 2023-06-07. Score: 9
AFAIK, most Gophers tend to replace generics with duplicating classes, merely changing the contained type and the name to reflect it. A similar approach is used in C, which has macros and therefore you can do something like this
#define INSTANTIATE_LIST(T) struct LinkedList_##T { T item; LinkedList_##T *next; }

INSTANTIATE_LIST(int)

LinkedList_int head = {0, NULL};

Using a buildsystem you can do something similar with golang's code generation tools: https://go.dev/blog/generate
> **Comments:**
> **username_7** (2023-06-08): So, essentially, roll your own templates?  
> **username_6** (2023-06-08): @UnrelatedString yeah, not the greatest solution but people definitely do it in C and Go.  
---
### Answer by username_2 on 2023-06-07. Score: 6
Go always had generics!
Except they were limited to built-in types and had special syntax support.
Generic arrays are spelt [n]a, slices []a, generic maps map[k]v, and channels chan a.
They also are created either by special literals or by the make feature.
This makes your example look like
x := make([]int)
x = append(x, 1)
var y int
y = x[0]

Now, how to have generic user types is a question Go (and other similar languages) just didn't answer, instead opting to leaving you to your own devices.
---
### Answer by username_1 on 2023-06-08. Score: 5
Using interface{} and type assertions
Go allows for dynamic dispatch using interfaces, which contain the methods needed for a type to implement said interface. As a special case, the empty interface interface{} specifies that a type needs to match no methods to be considered as implementing the empty interface; therefore, all types implement the empty interface.
Through this, pseudo-generics and heterogenous data structures (like JSON's heterogenous arrays) are possible.
This allows something like the following, where an interface{} is used to store values of different types in a slice. These types can be differentiated using a switch over the type, or with type assertions to a different type.
nodes := []any{12345, "hello world!", 5.678}
// note: any is a builtin alias for interface{}

for _, n := range nodes {
    switch n := n.(type) {
        case int: println(n-1) // n is an int
        case string: println(n + "foobar") // n is a string
        default: println("unknown") // everything else
    }
}

However, with type assertions, you will get a runtime error if you are unable to explicitly cast between the two types. In the above example, nodes[1].(int) will panic because a string cannot be casted into an int.
> **Comments:**
> **username_0** (2023-06-08): That looks messy  
> **username_1** (2023-06-08): @Seggan because it is haha. Though, as other answers have mentioned, the builtin slice/array, map, and channel types are enough for most needs. Anything more complicated tends to be placed into structs.  
---
### Answer by username_3 on 2023-06-07. Score: 3
Disclaimer: I don't have much experience in Go, and this answer is written from the limited experience I have. If I've got anything wrong in this answer, please leave a comment and I'll try to correct the answer.

Go doesn't have a list type
That's right. You only have arrays, which need an element type:
x := []int {1, 2, 3, 4};

Nested arrays work the same way:
x := [][]int {{1, 2}, {3, 4}, {5, 6}};

Then, it's obvious to the compiler what type x[0] is. This means you don't need to explicitly specify the type. This would be perfectly fine:
y := x[0];


But what if it did?
The only option that I can think of is to get the programmer to explicitly put the type in the definition, like this:
var y int = x[0];

And then at runtime try and convert x[0] to an integer. If that fails, raise a runtime error.
This isn't great though, so it's probably better just to stick to arrays, and not lists.
> **Comments:**
> **username_4** (2023-06-07): I think this answer is side-stepping the question. Lists are an example where generics are useful, and it follows from the fact that Go has no generics that Go has no generic list type. The question is how can you have things like lists in Go, when those things would require generics in other languages; so "the standard library doesn't have them" isn't an answer. If the answer is "you *can't* have them" then it would be better to spell that out, but in that case the answer is really that Go *doesn't* circumvent the lack of generics, and that this is a major limitation in the language.  
> **username_4** (2023-06-07): OTOH, if the answer is "you can't have them, but you don't need them because XYZ", then it might not be a major limitation in the language. But if XYZ is just arrays then that is still a pretty major limitation, because arrays aren't a good substitute for lists, and arrays aren't a substitute at all for many other things where generics are useful.  
---
