Welcome to new things

[Technical] [Electronic work] [Gadget] [Game] memo writing

Notes on what I got into with Go language (interface)

I started Golang.

Golang, like C, has a simple syntax and data structure, but it also incorporates advanced concepts to increase productivity.

And for that reason, Golang has some slightly tricky syntax.

However, without knowing them, I made judgments based on my preconceptions and self-serving assumptions in other languages, and got into a bit of trouble.

Here, I would like to note down some of the things that I personally got into or misunderstood when I started Golang, as a reminder by topic.

In this issue, we make a note on what an "interface" is.

What is an interface?

Golang variables only have values, not type information, and Golang has a built-in "special structure" that holds a set of variable values and their type information, which is called an interface.

Since an interface can store variables of any type, an interface is also a so-called general-purpose variable container.

What is the interface used for?

One of Golang's goals in introducing the interface was to make it possible to write generic functions.

Because Golang is a statically typed language, when a variable is used, the type of the variable must be determined.

However, when trying to write a generic function, it is not possible to write a process for an unknown type.

Therefore, we provide a type that can store variables of any type, or interface, and write processing for that interface so that a generic function can be written.

Interface usage (empty interface)

Since interfaces are general-purpose containers, they can be assigned variables of any type. Such an interface that can store anything is called an empty interface.

For example, the following interface can be assigned values of both types "int" and "bool".

var i interface{}

a := 123
i = a
fmt.Println(i)  // 123

b := true
i = b
fmt.Println(i)  // true

However, since the interface is a structure and values are stored in that structure, values must be retrieved from the interface in order to use them.

To retrieve a value from an interface, specify it with <interface>. (type), which is called a type assertion.

var i1, i2 interface{}

i1 = 1
i2 = 2
fmt.Println(i1 + i2)  // I can't do this.

fmt.Println(i1.(int) + i2.(int)) // 3

How the interface works

The interface is a 16-byte structure consisting of a pointer to type information and a pointer to value information, as shown below.

type emptyInterface struct {
    typ  *rtype
    word unsafe.Pointer
}

When a value is assigned to an interface, an area is allocated to store the value, the value is assigned to that area, and the address is set to a pointer to the value area of the interface structure.

For example, a value assigned to an interface can be directly retrieved.

package main

import (
    "fmt"
    "unsafe"
)

type emptyInterface struct {
    typ  unsafe.Pointer
    word unsafe.Pointer  // The value assigned here is stored.
}

func main() {

    var i interface{}

    a := "TEST"
    i = a

    p := (*string)(((*emptyInterface)(unsafe.Pointer(&i))).word)
    fmt.Println(*p) // TEST
}

Interface Notes

When I first saw the interface, I thought it was like a generic pointer, but it was not.

When an assignment is made to an interface, what is stored in the interface is the same as what is done in the variable assignment "=", which is a "copy of the value".

For example, if the variable int is assigned to an interface, the value of the int variable is stored, not the variable used for the assignment. Similarly, when a structure is assigned, a copy of the structure is stored, and so on.

If you want to store the variable used for assignment, you must assign a pointer to the variable.

var i interface{}

a := 123
i = a
i.(int) = 0 // This cannot be done. Error.

i = &a
*i.(*int) = 0 // This can be done. a is rewritten.
fmt.Println(a) // 0

Interface as method type definition

These are the uses of the interface as a generic container (empty interface).

There is one more use for an interface: as a type that defines what methods a type has.

It is possible to call a method on a variable directly from the interface without specifying the type of the stored variable.

It allows you to call a variable's method in a generic function without leaving the interface.

However, in order to do so, it is necessary to define what kind of methods the interface has as an interface.

For example, as shown in A Tour of Go, let's define an interface "Stringer with a method that returns string with String() and write a generic function that outputs a string using Stringer.

type Stringer interface {
    String() string
}

func printString(i Stringer){
   fmt.Println(i.String())
}

To make any structure "Stringer", define the String() method.

It can then be called by the generic function printString() written using the Stringer interface.

package main

import (
    "fmt"
)

// Any type
type myStruct struct {
    str string
}

// Corresponding any type to the Stringer interface
func (o myStruct) String() string {
    return o.str
}

// Stringer Interface
type Stringer interface {
    String() string
}

// Generic functions using the Stringer interface
func printString(i Stringer) {
    fmt.Println(i.String())
}

func main() {
    a := myStruct{str: "TEST"}
    printString(a) // TEST
}

What is interesting here is that in order for a type to have an interface, it is not necessary to include the interface in the type definition, but only to define the same methods that are defined in the interface, You only need to define the same methods defined in the interface to be considered to have that interface.

What is even more interesting is that you can also take another interface out of an arbitrary interface.

For example, from an empty interface, the "Stringer" interface described above can be extracted.

func printString(i interface{}) {
    switch v := i.(type) {
    case Stringer:
        fmt.Println(v.String())
    default:
        fmt.Println("null")
    }
}

Common interface uses

If an interface is disclosed in a library or package, and you create a type that satisfies that interface, it will perform processing according to that interface.

For example, with the "Stringer" interface described above, you can create your own structure with

type Stringer interface {
    String() string
}

If you define a method that satisfies fmt.Println(), passing the structure in fmt.Println() will output the string returned by String().

The other is its use as a general-purpose container.

For example, if you are parsing JSON and do not know what values it contains, you cannot predefine the type.

In such cases, you can pass an interface and have the values stored there.

package main

import (
    "encoding/json"
    "fmt"
)

func main() {

    jsonStr := "[{\"id\": 123, \"name\": \"TEST\"}]"

    var i interface{}
    json.Unmarshal([]byte(jsonStr), &i)
    fmt.Println(i) // [map[ id:123 name:TEST]]
}

reflect

When writing generic functions using interfaces, you will be reading and writing interface values, and "pkg/reflect" is a package that makes them easier to handle.

Impressions, etc.

I was confused at first because the interface has two roles: as a generic container and as a method definition.

I didn't really understand what assigning a value to an interface meant, but the following article cleared it up. In short, you were assigning values to a variable container (interface).

Once I understood that, I understood that bringing an interface to a function argument is, in essence, the same as assigning a value to an interface.

func test(i interface{}){
    fmt.Println(i)
}

test(123) // i = 123.

I also now understand why assigning a variable to an interface does not change the value of the original variable from the interface.

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com