Read the usage scenario of Go anonymous structure

Posted by Paul Moran on Sat, 11 Dec 2021 10:03:02 +0100

preface

Anonymous behavior is very common in go language, such as:

  • Anonymous function: that is, what we know as closure
  • Anonymous fields in structure
  • Anonymous structures

The design of anonymous behavior brings some difficulties in understanding, but after you are familiar with the use of anonymous design, you will find that anonymous design can help you write more concise, elegant, efficient and secure code in some specific scenarios.

What is an anonymous structure

Anonymous structure: as the name suggests, the structure is not named. For example, the following code example:

// example1.go
package main

import (
    "fmt"
)

func main() {
    a := struct{name string; age int}{"bob", 10}
    b := struct{
        school string
        city string
    }{"THU", "Beijing"}
    fmt.Println(a, b)
}

In this example, we define two variables a and b, which are anonymous structure variables.

Common usage scenarios

Global variable combination

Sometimes we define several global variables in the program. The meanings of some global variables are interrelated. At this time, we can use anonymous structures to combine these associated global variables.

// example2.go
package main

import "fmt"

// DBConfig declares global anonymous struct variables
var DBConfig struct {
    user string
    pwd string
    host string
    port int
    db string
}

// SysConfig global anonymous structure variables can also be initialized and assigned directly when declared
var SysConfig = struct{
    sysName string
    mode string
}{"tutorial", "debug"}

func main() {
    // Assign a value to the anonymous structure variable DBConfig
    DBConfig.user = "root"
    DBConfig.pwd = "root"
    DBConfig.host = "127.0.0.1"
    DBConfig.port = 3306
    DBConfig.db = "test_db"
    fmt.Println(DBConfig)
}

After the global anonymous structure variable is assigned, subsequent codes can use this anonymous structure variable.

Note: if your program wants to create multiple variables for a global structure, you can't use anonymous structures.

Local variable combination

Global variables can be combined, and local variables can also be combined.

If the meanings of some variables are related to each other in a local scope (such as a function or method body), they can be combined into a structure.

At the same time, this structure is only temporary and disposable. You do not need to create multiple variables of this structure, so you can use anonymous structure.

// example3.go
package main

import "fmt"

func main() {
    // As local anonymous structure variables, a and b are only temporarily used once
    // Note: a is to put the field declaration in struct on the same line, and the fields should be separated by semicolons, otherwise the compilation will report an error
    a := struct{name string; age int}{"Alice", 16}
    fmt.Println(a)

    b := struct{
        school string
        city string
    }{"THU", "Beijing"}
    fmt.Println(b)
}

Build test data

Anonymous structures can be used in conjunction with slices and are often used to create a set of test data.

// example4.go
package main

import "fmt"

// test data
var indexRuneTests = []struct {
    s    string
    rune rune
    out  int
}{
    {"a A x", 'A', 2},
    {"some_text=some_value", '=', 9},
    {"☺a", 'a', 3},
    {"a☻☺b", '☺', 4},
}

func main() {
    fmt.Println(indexRuneTests)
}

Nested lock

We often encounter multiple goroutine s to operate shared variables. For concurrency security, we need to lock the read and write of shared variables.

At this time, it is usually necessary to define a lock matched with the shared variable to protect the shared variable.

The combination of anonymous structure and anonymous field can write more elegant code to protect the shared variables in anonymous structure and realize concurrency security.

// example5.go
package main

import (
    "fmt"
    "sync"
)

// hits anonymous struct variable
// Here we use both anonymous structure and anonymous field, sync Mutex is an anonymous field
// Because the anonymous structure is nested with sync Mutex, so there's sync Mutex's Lock and Unlock methods
var hits struct {
    sync.Mutex
    n int
}

func main() {
    var wg sync.WaitGroup
    N := 100
    // Start 100 goroutine s to read and write members n of the anonymous structure at the same time
    wg.Add(N)
    for i:=0; i<100; i++ {
        go func() {
            defer wg.Done()
            hits.Lock()
            defer hits.Unlock()
            hits.n++
        }()
    }
    wg.Wait()
    fmt.Println(hits.n) // 100
}

JSON serialization and deserialization in HTTP processing functions

When dealing with http requests, we usually deal with JSON data.

For example, when the content type of the post request uses application/json, the JSON data received by the server is in the format of key:value. The value types of different keys can be different, which may be numbers, strings, arrays, etc. Therefore, the use of JSON Unmarshal and map[string]interface {} to receive the data after JSON deserialization.

However, using map[string]interface {} has several problems:

  • There is no type check: for example, a value of json was expected to be of string type, but the request passed in bool type. Use json Unmarshal will not report an error when parsing to map[string]interface {}, because an empty interface can accept any type of data.
  • The map is fuzzy: after Unmarshal gets the map, we have to judge whether the key exists in the map. Otherwise, if you take the value of a nonexistent key, you may get the nil value. If you don't check it and directly * the nil pointer, panic will be triggered.
  • The code is lengthy: you must first judge whether the key exists. If so, you must display and convert it to the corresponding data type, and judge whether the conversion is successful. The code will be lengthy.

At this time, we can use the anonymous structure to receive the deserialized data, and the code will be more concise. See the following code example:

// example6.go
// Request command: curl - x post - H "content type: application / JSON" http://localhost:4000/user  -d '{"name":"John", "age":111}'
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
)

func AddUser(w http.ResponseWriter, r *http.Request) {
    // The data anonymous structure variable is used to receive json data sent by http requests
    data := struct{
        Name string `json:"name"`
        Age int    `json:"age"`
    }{}
    // Deserialize json data into the data variable
    decoder := json.NewDecoder(r.Body)
    err := decoder.Decode(&data)
    if err != nil {
        fmt.Println("error:", err)
    }
    fmt.Println(data)
    fmt.Fprint(w, "Hello!")
}

func index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "index")
}

func main() {
    http.HandleFunc("/", index)
    http.HandleFunc("/user", AddUser)
    log.Fatal(http.ListenAndServe("localhost:4000", nil))
}

summary

Anonymous structure allows us not to define structure type first and then structure variable. The structure definition and variable definition can be combined and completed at one time.

Anonymous structures have the following application scenarios:

  • Combined variables:

    • Global anonymous structure: group the associated global variables together
    • Local anonymous structures: temporary disposable
  • Build test data:

    • Anonymous structure + slice to construct a set of test data
  • Nested locks: combine the shared access data of multiple goroutine s and the locks protecting shared data into an anonymous structure, making the code more elegant.
  • Json serialization and deserialization in HTTP processing function:

    • Anonymous structure variables are used to receive http request data
    • Compared with map[string]interface {}, the code is simpler and safer

Open source address

Document and code source address: https://github.com/jincheng9/...

You are also welcome to pay attention to the official account: coding is advanced, learning more Go, micro service and cloud native architecture.

References

Topics: Go Programming goroutine struct