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.