SoFunction
Updated on 2025-03-05

Golang determines that the structure is empty

How to determine whether the golang structure is empty

How to determine that golang structure is empty? It is to determine whether it has been initialized

The method is as follows:

You can use if objectA== (structname{}){ // your code } to make judgments.

The sample code is as follows:

package main

import (
    "fmt"
    "reflect"
)


type A struct{
    name string
    age int
}


func (a A) IsEmpty() bool {
    return (a, A{})
}


func main() {
    var a A
    if a == (A{}) {  // Can't go to brackets        ("a == A{} empty")
    }

    if () {
        ("reflect deep is empty")
    }
}

golang empty interface empty structure

Empty interface

  • An empty interface is a special form of interface type. There is no method for an empty interface, so no empty interface is required for any type. From an implementation point of view, any value meets the needs of this interface. Therefore, the empty interface type can save any value, or the original value can be taken out from the empty interface.
  • The empty interface type is similar to Object in C# or Java language, void* in C language, and std::any in C++. Before generics and templates appeared, empty interfaces were a very flexible way to save and use data abstractions and use them.

The internal implementation of the empty interface saves the object's type and pointer. The process of saving a data using an empty interface will be slightly slower than directly using variables of the corresponding type of data. Therefore, in development, you should use empty interfaces where you need them, rather than empty interfaces everywhere.

Variables of empty interface type can store variables of any type. Even the receiving pointer type uses interface{} instead of *interface{}.

Never use a pointer to point to an interface type, because it is already a pointer.

package main
 
import "fmt"
 
func main() {
    // Define an empty interface x    var x interface{}
    s := ""
    x = s
    ("type:%T value:%v\n", x, x)
    i := 100
    x = i
    ("type:%T value:%v\n", x, x)
    b := true
    x = b
    ("type:%T value:%v\n", x, x)
}

Empty interface memory allocation

​Will there be additional heap memory allocation in var i interface{} = a in Go 1.15?

var a  int = 3
// Are there any additional memory allocations below?var i interface{} = a

​Go 1.15 mentioned an interesting improvement in the runtime section: converting small integers to interface values ​​no longer requires memory allocation. A small integer refers to a number between 0 and 255.

Application of empty interface

1. Empty interface as a parameter of a function

Using an empty interface implementation can receive any type of function parameters.

// Empty interface as function parameterfunc show(a interface{}) {
    ("type:%T value:%v\n", a, a)
}
 
//The number of parameters of the function and the type of each parameter are not fixed.func myfunc(args ...interface{}) {
}

2. The empty interface is used as various types of values ​​of map, array, and slice

func main() {
    // Empty interface as map value    var studentInfo = make(map[string]interface{})
    studentInfo["name"] = "Li Bai"     //string
    studentInfo["age"] = 18        //int
    studentInfo["height"] = 1.82   //float
    studentInfo["married"] = false //bool
    (studentInfo)
 
    var a = new([3]interface{})
    a[0] = "Hello,World"
    a[1] = 32
    for _, b := range a {
        ("%v\t%[1]T\n", b)
    }
}

3. Type Assertion

The value of an interface (referred to as an interface value) is composed of two parts: a specific type and a specific type of value. These two parts are called the dynamic type and dynamic value of the interface respectively.

func main() {
    var x interface{}
    x = ""
    v, ok := x.(string)
    if ok {
        (v)
    } else {
        ("Type assertion failed")
    }
}

4. Type judgment

func justifyType(any interface{}) {
    switch v := any.(type) {
    case string:
        ("any is a string,value is: %v\n", v)
    case int:
        ("any is a int is: %v\n", v)
    case bool:
        ("any is a bool is: %v\n", v)
    case float32, float64:
        ("any is a float is: %v\n", v)
    default:
        ("unsupport type:%T is: %v\n", v, v)
    }
}
 
func main() {
    var x interface{}
    x = ""
    justifyType(x)
    x = 0.1
    justifyType(x)
}

Because empty interfaces can store the characteristics of any type of value, empty interfaces are widely used in the Go language.

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:

type empty struct{}

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.

Memory occupancy 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 of the type is judged, the zerobase address is fixedly returned.

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.

For example, when using map to represent a collection, only focus on the key, and value can use struct{} as the placeholder. If you use other types as placeholders, such as int, bool, not only is memory wasteful, but it is also prone to ambiguity.

// Define a Set collection that saves the string typetype Set map[string]struct{}
 
// Add an elementfunc (s Set) Add(key string) {
    s[key] = struct{}{} //The second {} represents the assignment}
 
// 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"))
}
 
func min(a int, b uint) {
    var min = 0
    if a < 0 {
        min = a
    } else {
        min = copy(make([]struct{}, a), make([]struct{}, b))
    }
    ("The min of %d and %d is %d\n", a, b, min)
}

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
        }
    }
}

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.

Summarize

The above is personal experience. I hope you can give you a reference and I hope you can support me more.