Easyread
Published in

Easyread

Golang Series — Empty Struct

Empty struct? What is it? When we use and what the advantages of using an empty struct?

Photo by pixabay

Hi everyone! At this time I want to share some experience, thought, or opinions about technology-related to the software engineering field. However, my article could be a light topic and just come in short. I want to share my experience and opinion about using empty struct in Golang.

In the past, I usually found conditions that are made me use key only without value, for example having a unique key on a slice. Back then, I always use a map with bool key. Here is the way if I want to get unique int:

func UniqueInt(slice []int) []int {
mapInt := make(map[int]bool)
for _, v := range slice {
mapInt[v] = true
}

output := []int{}

for v := range mapInt{
output= append(output, v)
}

return output
}

However, sometimes I see some implementation using empty struct struct{} . Why we should use this and what the advantages?

Starting from the size, empty struct have the lowest size compared to others. Here the example code for getting the size:

package mainimport (
"fmt"
"unsafe"
)

func main() {
var s struct{}
var i interface{}
var b bool
fmt.Println(unsafe.Sizeof(s), unsafe.Sizeof(i), unsafe.Sizeof(b))
}

The output:

0 16 1

As we can see, empty struct has 0 . And bool that I always use has 1 value.

Next, about memory usage. I’ve found some pieces of code (the references link can be found on the References part) and try to modify them to get the result from empty struct and bool as well. See the code below:

package main

import (
"fmt"
"runtime"
)

func main() {
// Below is an example of using our PrintMemUsage() function
// Print our starting memory usage (should be around 0mb)
fmt.Println("Start")
PrintMemUsage()
fmt.Println("")

structContainer := make(map[int]struct{}, 1000000)
for i := 0; i < 1000000; i++ {
structContainer[i] = struct{}{}
}

fmt.Println("With 1kk empty struct{}")
PrintMemUsage()
fmt.Println("")

nilContainer := make(map[int]interface{}, 1000000)
for i := 0; i < 1000000; i++ {
nilContainer[i] = nil
}

fmt.Println("With 1kk nil interface{}")
PrintMemUsage()
fmt.Println("")

boolContainer := make(map[int]bool, 1000000)
for i := 0; i < 1000000; i++ {
boolContainer[i] = true
}

fmt.Println("With 1kk true bool")
PrintMemUsage()
fmt.Println("")

}

// PrintMemUsage outputs the current, total and OS memory being used. As well as the number
// of garage collection cycles completed.
func PrintMemUsage() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
// For info on each, see: https://golang.org/pkg/runtime/#MemStats
fmt.Printf("Alloc = %v KiB", bToMb(m.Alloc))
fmt.Printf("\tTotalAlloc = %v KiB", bToMb(m.TotalAlloc))
fmt.Printf("\tSys = %v KiB", bToMb(m.Sys))
fmt.Printf("\tNumGC = %v\n", m.NumGC)
}

func bToMb(b uint64) uint64 {
return b / 1024
}

When we run the code above, we will get this result:

Start
Alloc = 84 KiB TotalAlloc = 84 KiB Sys = 69714 KiB NumGC = 0
With 1kk struct{}
Alloc = 21986 KiB TotalAlloc = 21999 KiB Sys = 71440 KiB NumGC = 1
With 1kk nil interface{}
Alloc = 56649 KiB TotalAlloc = 78576 KiB Sys = 139154 KiB NumGC = 2
With 1kk bool
Alloc = 80738 KiB TotalAlloc = 102665 KiB Sys = 139154 KiB NumGC = 2

From the data above, we can say empty struct has the lowest memory usage due to Sys value. From the doc, Sys is the total bytes of memory obtained from the OS.

Then we can modify the code to find unique int like this:

func UniqueInt(slice []int) []int {
mapInt := make(map[int]struct{})
for _, v := range slice {
mapInt[v] = struct{}{}
}

output := []int{}

for v := range mapInt{
output= append(output, v)
}

return output
}

Thank you for reading my article, hopefully, it would be useful for you! See you at the next topic!

--

--

--

Easy read, easy understanding. A good writing is a writing that can be understood in easy ways

Recommended from Medium

Surfing the Tsunami: Adopting Digital Workflows

Setting up Spring Application Specifying Application Context using XML Part 3

Kishore Gopalan’s Guide to #GoogleCloudNext 2021

GitOps with github: DOA.

Hand-picked Programming Articles From Medium — April 2022

May the 4th be with you

Interpreted Languages and Hybrid Implementation Systems

Tolar v1.1 update was successful!

How to connect an existing Active Directory to Huawei Cloud Workspaces

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Bismo Baruno

Bismo Baruno

Software Engineer | Traveler | Guitarist | J-Lovers

More from Medium

Generics in Golang.

gorilla/mux 101 (rk-boot):Add tracing middleware

Hacking sum types with Go generics

SOLID Principles of Object-Oriented Design in GoLang (Part-3)