Preface
During the development process of using the Go language, we inevitably define structures, but the structures we define all have fields, and basically no empty structures that do not contain fields. You might ask the other way around, what is the use of an empty structure without fields? Then let’s study the empty structure in this article!
Note: This article is based on go 1.14.4 analysis
What is an empty structure
We say that a structure that does not contain any fields is called an empty structure. An empty structure can be defined in the following ways:
Native definition
var a struct{}
Type alias
type empty struct{} var e empty
Features
The address is the same
We define two non-empty structures and empty structure variables respectively, and then take the address to print them. We find that the addresses of the empty structure variables are the same:
// Define a non-empty structuretype User struct { name string } func main() { // The variable addresses of two non-empty structures are different var user1 User var user2 User ("%p \n", &user1) // 0xc000318670 ("%p \n", &user2) // 0xc000318680 // Define two empty structures with the same address var first struct{} var second struct{} ("%p \n", &first) // 0x1ca15f0 ("%p \n", &second) // 0x1ca15f0 }
We know that variable transfers in Go are all value transfers. The variable addresses before and after passing parameters should be different. Let’s try it again by passing parameters:
// Non-empty structuretype NonEmptyUser struct { name string } // Empty structuretype EmptyUser struct{} // Print the parameter address of non-empty structurefunc testNonEmptyUser(user NonEmptyUser) { ("%p \n", &user) } // Print empty structure parameter addressfunc testEmptyUser(user EmptyUser) { ("%p \n", &user) } func main() { // The variable addresses of two non-empty structures are different var user1 NonEmptyUser ("%p \n", &user1) // 0xc0001986c0 testNonEmptyUser(user1) // 0xc0001986d0 // The addresses of two empty structure variables are the same var user2 EmptyUser ("%p \n", &user2) // 0x1ca25f0 testEmptyUser(user2) // 0x1ca25f0 }
It was found that for non-empty structures, the addresses before and after the parameter transmission are different, but for empty structure variables, the addresses before and after are the same.
Size is 0
In Go, we can use : to calculate the number of bytes occupied by a variable, so let’s take a few examples:
type EmptyUser struct{} func main() { var i int var s string var m []string var u EmptyUser ((i)) // 8 ((s)) // 16 ((m)) // 24 ((u)) // 0 }
You can see that the memory space occupied by an empty structure is 0, and the space occupied by a combination of an empty structure is also 0:
// Combination of empty structurestype EmptyUser struct { name struct{} age struct{} } func main() { var u EmptyUser ((u)) // 0 }
Principle Exploration
Why are the addresses of empty structures the same and the size is 0? Let's take a look at the source code (go/src/runtime/):
// base address for all 0-byte allocations var zerobase uintptr // When creating a new object, call mallocgc to allocate memoryfunc newobject(typ *_type) { return mallocgc(, typ, true) } func mallocgc(size uintptr, typ *_type, needzero bool) { if gcphase == _GCmarktermination { throw("mallocgc called with gcphase == _GCmarktermination") } if size == 0 { return (&zerobase) } ...... }
From the source code, we can see that when creating a new object, we need to call () for memory allocation and further call the mallocgc method. In this method, if the size==0 of the type is determined, the address of zerobase is returned regularly. zerobase is a uintptr global variable that takes up 8 bytes.
So what we can be sure is that in Go language, all memory allocations for size==0 use the same address &zerobase, so all empty structure addresses we see at the beginning are the same.
Use scenarios
If an empty structure does not contain any data, then its application scenario should not care about the value content and just treat it as a placeholder. In this scenario, since it does not occupy memory space, using an empty structure can not only save space, but also provide semantic support.
Set (Set)
Students who have used Java should have used the Set type. Set is a collection that holds no duplicate elements, but Go does not provide native Set type. However, we know that the Map structure stores the key-value type, and the key does not allow duplication, so we can use Map to implement Set. The key stores the required data, and just give a fixed value to the value. So what value is good for value? At this time, our empty structure can appear, without occupying space, and can also complete the placeholding operation, which is perfect. Let's see how to achieve it.
// Define a Set collection that saves the string typetype Set map[string]struct{} // Add an elementfunc (s Set) Add(key string) { s[key] = struct{}{} } // Remove an elementfunc (s Set) Remove(key string) { delete(s, key) } // Whether to include an elementfunc (s Set) Contains(key string) bool { _, ok := s[key] return ok } // Initializationfunc NewSet() Set { s := make(Set) return s }
// Test usefunc main() { set := NewSet() ("hello") ("world") (("hello")) ("hello") (("hello")) }
Signal transmission in channel
An empty structure and channel are a classic combination. Sometimes we just need a signal to control the running logic of the program and don’t care about its content.
In the following example, we define two channels to receive signals completed by two tasks. When a signal completed by the task is received, the corresponding action will be triggered.
func doTask1(ch chan struct{}) { () ("do task1") ch <- struct{}{} } func doTask2(ch chan struct{}) { ( * 2) ("do task2") ch <- struct{}{} } func main() { ch1 := make(chan struct{}) ch2 := make(chan struct{}) go doTask1(ch1) go doTask2(ch2) for { select { case <-ch1: ("task1 done") case <-ch2: ("task2 done") case <-( * 5): ("after 5 seconds") return } } }
Summarize
In this article, we have learned the following content:
- An empty structure is a special structure that does not contain any elements
- The size of the empty structure is 0
- The empty structures are all the same
- Since the empty structure does not occupy space, it is suitable for realizing the Set structure, transmitting signals in the channel, etc. from the perspective of saving memory.
This is the end of this article about the detailed explanation of Go language empty structure. For more related contents of Go language empty structure, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!