Go1.18 version generic details

1. Introduction to generics

Today is December 16, 2021. The day before yesterday, that is, on the 14th, Golang officially released go1 In the Beta version of 18, the heaviest update is generics

What are generics? Some small partners may not know the concept. In fact, generics are products equivalent to templates in C + +

For example:

vector<int> v;
v.push_back(1);

The int in specifies that the type of storage element in v is int, which can also be replaced with other types you want to store

To customize a template, you only need to:

template<class T>
class Vector{
    int mLen;
    T* data;
};

Another example is the concept of generics in Java:

class Vector<T>{
    public void push_back(T val){
        //implement...
    }
}

Today, Go also has its own generics, and personally believes that Go's generics are more useful than other languages (after all, Go's generics have been developed in recent years, absorbing the essence of many high-level languages).

It looks like this:

func MyPrintln[T any](a T) {
	fmt.Println(a)
}

func main() {
	MyPrintln(1)
	MyPrintln("Xiao Wang")
	MyPrintln([]int{3, 2, 1})
    //Operation results:
	//1
	//Xiao Wang
	//[3 2 1]
}

2. Detailed explanation of generic syntax

Let's start by detailing the syntax of generics

MyType[T1 constraint1 | constraint2, T2 constraint3...] ... 

The syntax of generics is very simple, similar to the above, where:

  • MyType can be function name, structure name, type name
  • T1, T2... Are generic names, which can be taken arbitrarily
  • Constraint means constraint, which is also the most important concept in generics. I will explain it in detail next
  • Use | to separate multiple constraints, and t can satisfy one of them (for example, T1 can be any of constraint1 and constraint2)

2.1 what is a constraint

Constraint means to limit the scope. The function of constraint is to limit the scope and limit T to a certain range

The commonly used scope, we will naturally think of:

  • any(interface {}, any type can receive, how convenient!)
  • Interger (all ints, how convenient, Int64, int32...)
  • Float (ditto)
  • Comparable (for all types that can be compared, we can customize some methods for all types that can be compared)
  • ...

These constraints are either officially defined as built-in types or are covered in the constraints package!!!

Here is builtin Part of the official source code of go:

// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}

// comparable is an interface that is implemented by all comparable types
// (booleans, numbers, strings, pointers, channels, interfaces,
// arrays of comparable types, structs whose fields are all comparable types).
// The comparable interface may only be used as a type parameter constraint,
// not as the type of a variable.
type comparable comparable

Here are constraints Part of the official source code of go:

// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
	Signed | Unsigned
}

// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
	~float32 | ~float64
}
//......

It can be seen that the official is still very considerate. Many wheels have been built for us

By observing the constraints package and reading the official documentation, I also learned how to customize constraints

2.2 custom constraint

The following is the official source code in the constraints package:

type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

The Signed constraint is written in this way. The points we need to get are as follows:

  • Use interface {} to customize constraints
  • Use | to include different types in the constraint. For example, int, int8 and Int64 all meet the Signed constraint
  • You may wonder, ~ what is it??? Int i know, ~ int i don't know??? It doesn't matter. Actually ~ it's very simple. It means fuzzy matching, for example:
    • type MyInt int64
    • At this time, MyInt is not equivalent to int64 type (Go language feature)
    • If we use int64 to constrain Myint, Myint does not satisfy the constraint
    • If we use ~ int64 to constrain Myint, Myint satisfies the constraint (that is, ~ int64 only requires that the underlying layer of this type is int64, that is, fuzzy matching)
    • For the sake of robustness, the official naturally added all types to the front~

Let's customize a constraint

type My_64_Bits_Long_Num interface {
	~int64 | ~float64
}

Isn't it simple?

3. Generic comprehensive use cases

3.1 custom types

Here you can customize a map

//The key of the map must be comparable, that is, it can be = = and= Comparison (for handling hash conflicts)
type MyMap[K comparable, V constraints.Integer | constraints.Float] map[K]V

func main() {
    m := make(MyMap[string, int])
    m["elder male cousin"] = 100
    m["Xiao Zhang"] = 0
    for k, v := range m{
        fmt.Printf("key: %v, val: %v\n", k, v)
    }
}

3.2 custom structure

Here is a handwritten linked list (which can only store integers) as an example

type MyIntergerNode[T constraints.Integer] struct {
	Next *MyIntergerNode[T]//Note that type declarations must be added here (as in C + +)
	Data T
}

func main() {
	head := &MyIntergerNode[int64]{Next: nil, Data: 1}
	head.Next = &MyIntergerNode[int64]{Next: nil, Data: 2}

	for p := head; p != nil; p = p.Next{
		fmt.Printf("%d ", p.Data)
	}
}

3.3 user defined functions

Here is a custom function for comparing types of 64 bit size

//Just customized constraints
type My_64_Bits_Long_Num interface {
    ~int64 | ~float64
}

func MyCompare[T My_64_Bits_Long_Num](a, b T) bool {
    return a < b
}

func main() {
    var a int64 = 1
    var b int64 = 8

    //Functions can omit parameter types that are not written (syntax sugar)
    ans := MyCompare(a, b)
    if ans{
        fmt.Printf("%v less than%v", a, b)
    }else{
        fmt.Printf("%v greater than%v", a, b)
    }
}

4. Summary

Burst the liver for an hour, and finally the liver came out of this article

Go has been waiting for generics for a long time. With generics, go can create more powerful containers in the future. Even slice and map can be rewritten into a generic version (see the official intention)

In short, I hope you can master the generics of go after reading this article (Go1.18 will be officially launched in February next year and can be applied to the production environment)

If you learn it, would you mind giving a free praise 😁

Thanks for reading. Bye~~

Keywords: Go Back-end

Added by gladiator83x on Thu, 16 Dec 2021 19:26:43 +0200