Rewriting a Golang Library with Generics


Generics, released with Golang 1.18, allow writing reusable functions and structures “templates” with types factored out. Without new generics support, users need to implement interface methods, e.g., sort, to use type agnostic libraries. In this article, the author looks at Golang generics through an example transition on a Go data structure library and points out the differences and benefits.

Generics Heap

Let’s look at the consumer side. The below code shows an earlier implementation that supports any type as an element of the Heap. Here, we are using the slice of interface{}, and we need to provide a comparison function to tell the library code how to order elements. Also, note the type assertions to int if we need to perform operations or call functions that take int after retrieving the elements as interface{}.

func ExampleMaxHeap() {
	iHeap := heap.NewMaxHeap([]interface{}{3, 7, 4, 4, 1}, lib.IntCompFunc)
	iHeap.Insert(9)

	// Calculate the sum of (rank * val) rank: 1-n
	sum := 0
	for rank := 1; !iHeap.IsEmpty(); rank++ {
		cur := iHeap.Extract().(int)
		fmt.Printf("Out: %d\n", cur)
		sum += cur * rank
	}

	fmt.Printf("Sum: %d\n", sum)

	// Output:
	// Out: 9
	// Out: 7
	// Out: 4
	// Out: 4
	// Out: 3
	// Out: 1
	// Sum: 72
}

We are using the new generics Heap[T] in the next code example. Now, we can pass a slice of int directly; we do not need to provide a comparison function. Furthermore, since we are getting original types, there is no need for the extra step of type assertion either. The result is a much more fluent, intuitive, and clean way of using the library.