diff --git a/examples.txt b/examples.txt
index c309511..a1e4ef3 100644
--- a/examples.txt
+++ b/examples.txt
@@ -20,6 +20,7 @@ Structs
Methods
Interfaces
Embedding
+Generics
Errors
Goroutines
Channels
diff --git a/examples/generics/generics.go b/examples/generics/generics.go
new file mode 100644
index 0000000..b1f5f99
--- /dev/null
+++ b/examples/generics/generics.go
@@ -0,0 +1,75 @@
+// Starting with version 1.18, Go has added support for
+// _generics_, also known as _type parameters_.
+// TODO: spec link?
+
+package main
+
+import "fmt"
+
+// As an example of a generic function, `MapKeys` takes
+// a map of any type and returns a slice of its keys.
+// This function has two type parameters - `K` and `V`;
+// `K` has the `comparable` _constraint_, meaning that
+// we can compare values of this type with the `==` and
+// `!=` operators. This is required for map keys in Go.
+// `V` has the `any` constraint, meaning that it's not
+// restricted in any way (`any` is an alias for `interface{}`).
+func MapKeys[K comparable, V any](m map[K]V) []K {
+ r := make([]K, 0, len(m))
+ for k := range m {
+ r = append(r, k)
+ }
+ return r
+}
+
+// As an example of a generic type, `List` is a
+// singly-linked list with values of any type.
+type List[T any] struct {
+ head, tail *element[T]
+}
+
+type element[T any] struct {
+ next *element[T]
+ val T
+}
+
+// We can define methods on generic types just like we
+// do on regular types, but we have to keep the type
+// parameters in place. The type is `List[T]`, not `List`.
+func (lst *List[T]) Push(v T) {
+ if lst.tail == nil {
+ lst.head = &element[T]{val: v}
+ lst.tail = lst.head
+ } else {
+ lst.tail.next = &element[T]{val: v}
+ lst.tail = lst.tail.next
+ }
+}
+
+func (lst *List[T]) GetAll() []T {
+ var elems []T
+ for e := lst.head; e != nil; e = e.next {
+ elems = append(elems, e.val)
+ }
+ return elems
+}
+
+func main() {
+ var m = map[int]string{1: "2", 2: "4", 4: "8"}
+
+ // When invoking generic functions, we can often rely
+ // on _type inference_. Note that we don't have to
+ // specify the types for `K` and `V` when
+ // calling `MapKeys` - the compiler infers them
+ // automatically.
+ fmt.Println("keys m:", MapKeys(m))
+
+ // ... though we could also specify them explicitly.
+ _ = MapKeys[int, string](m)
+
+ lst := List[int]{}
+ lst.Push(10)
+ lst.Push(13)
+ lst.Push(23)
+ fmt.Println("list:", lst.GetAll())
+}
diff --git a/examples/generics/generics.hash b/examples/generics/generics.hash
new file mode 100644
index 0000000..f70852a
--- /dev/null
+++ b/examples/generics/generics.hash
@@ -0,0 +1,2 @@
+2066b359af6eb554e2ab3c031ea23a4dd235a7a1
+afmUH4U_14g
diff --git a/examples/generics/generics.sh b/examples/generics/generics.sh
new file mode 100644
index 0000000..4e59904
--- /dev/null
+++ b/examples/generics/generics.sh
@@ -0,0 +1,2 @@
+keys: [4 1 2]
+list: [10 13 23]
diff --git a/public/embedding b/public/embedding
index 7e089e1..7630a2c 100644
--- a/public/embedding
+++ b/public/embedding
@@ -14,7 +14,7 @@
if (e.key == "ArrowRight") {
- window.location.href = 'errors';
+ window.location.href = 'generics';
}
}
@@ -230,7 +230,7 @@ we see that a container
now implements the
- Next example: Errors. + Next example: Generics.
diff --git a/public/errors b/public/errors index 4ac669a..e7340a7 100644 --- a/public/errors +++ b/public/errors @@ -9,7 +9,7 @@ onkeydown = (e) => { if (e.key == "ArrowLeft") { - window.location.href = 'embedding'; + window.location.href = 'generics'; } diff --git a/public/generics b/public/generics new file mode 100644 index 0000000..87a79ac --- /dev/null +++ b/public/generics @@ -0,0 +1,251 @@ + + + + ++ + | +
+
+ keys: [4 1 2] +list: [10 13 23]+ |
+
+ Next example: Errors. +
+ + + + +