Tianchi YU's Blog

Learning GoLang - Day 5 : Common Interfaces and Generics

To better use interfaces, you have to know more examples.

So, today I started to learn several useful and common interfaces in the standard library of Golang. Here is a short list of them, which are highly suggested to remember:

Stringer

type Stringer interface {
    String() string
}

Any type who has a method of String() string would implement this interface.

For example:

type Book struct {
    Title string
    Author string
}

func (b Book) String() string {
    return fmt.Sprintf("Book: $s - %s",  b.Title, b.Author)
}

Benefits

Wherever you see declaration in Go (such as a variable, function parameter or struct field) which has an interface type, you can use an object of any type so long as it satisfies the interface. ---- ALEX EDWARDS

Due to this distinctive characteristic of interfaces in Golang, any function utilizing the fmt.Stringer interface type as its parameter can accommodate any object that meets the fmt.Stringer interface criteria. This can be considered a form of genericity.

As outlined by Alex Edwards, there are three compelling reasons to utilize it:

You are free to check his blog to read more about it.

Empty Interface

An interface type in Go is kind of like a definition. It defines and describes the exact methods that some other type must have. ---- ALEX EDWARDS

When you see interface{} in a declaration, you can use an object of any type. Here is an example:

func main() {
    person := make(map[string]interface{}, 0)

    person["name"] = "Alice"
    person["age"] = 21
    person["height"] = 167.64

    fmt.Printf("%+v", person)
}

However, if you want to retrieve the value from an empty interface type, you have to type assert the value back to its original type before using it. For example, error will occur when you do person["age"] = person["age"] + 1. Explicit age, ok := person["age"].(int) allows you take age as an int type.

Even though, it is not suggested to use empty interfaces often, you could always define a specific struct with relevant typed fields for the above cases. It is useful only in situations where you need to work with unpredictable or user-defined types.

In the modern Go codebases(after Go 1.18), a new predeclared identifier any is introduced, it is actually a syntactic sugar that equivalent to interface{}.

Generics

Speaking of generics, you need to know type parameters. Type parameters can be used for functions or structures. For example, func Index[T comparable](s []T, x T) int is a function signature, and [T comparable] declares the type parameter and its property comparable. I am very impressed by the built-in approaches(or called constraints here). comparable constraint means the values of this type should support != and == operators. It helps to define the necessary behaviors of the introduced type.

And more generic types are supported simply by [T any].

Today's lesson has provided us with ample learning material. Tomorrow, I intend to delve into a new chapter, focusing more on the concurrency features of Golang.

#golang