Go Recipes - Examining Values

Table of Contents

Examining Values

These recipes offer some tips for examining Go values.

For other recipes see here.

Finding the memory used by a value

Summary

Go has no builtin sizeof operator such as we have in languages like C but the standard library has something that can help.

var v rune
fmt.Println(unsafe.Sizeof(v))

This uses the unsafe package which gives the size of the passed value in bytes. To see how much space is used by a different type you change the type of v.

Discussion

The unsafe package is, as the name suggests, not for general use; the documentation states:

Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines.

For the limited purpose of understanding how much space a particular data type requires it works just fine.

Note that the size reported is the size of the value itself not the size of any underlying data. Let's try with a string:

fmt.Println(unsafe.Sizeof("Hello, World!"))

This will report the size of the string argument not the string itself. Under the covers a string is a structure with a pointer to an array of bytes and a field holding the length. What gets reported is the size of this structure (16 bytes on my machine) not the amount of space being pointed to.

Now let's look at a struct:

s := struct{
      s string
      i int
      f float64
}{"Hello, World!", 42, 3.14}
fmt.Println(unsafe.Sizeof(s))

Again, this reports the size of the 's' object not the full amount of data that it uses (the bytes used to hold the string value are not reported).

You can see this more clearly with a slice:

type t struct{
	s string
	i int
	f float64
}

s1 := []t{ {"Hello, World!", 42, 3.14} }
fmt.Println(unsafe.Sizeof(s1))

s2 := []t{ {"Hello,", 40, 3.0}, {" World!", 2, 0.14} }
fmt.Println(unsafe.Sizeof(s2))

This reports the same value for each slice despite the second slice being twice as long. This is because the space used by the slice object is the same: a pointer, a length and a capacity in each case.

If you want to know how big the slice is you can use the built-in functions len and cap as follows:

s1 := []int{ 1, 2 }
fmt.Println(len(s1))
fmt.Println(cap(s1))

s2 := []int{ 1, 2, 3, 4 }
fmt.Println(len(s2))
fmt.Println(cap(s2))

s3 := make([]int, 0, 9)
fmt.Println(len(s3))
fmt.Println(cap(s3))

This shows the length and capacity of the slices. Note that the first two have the same capacity and length whereas the last slice constructed using the built-in make function is given a capacity of 9 entries and doesn't use any of them so it has a length of 0. To understand how much memory each of these will use we need to know how much space the slice object itself uses, how many slots have been allocated and how big each of the objects in the slots are. Like this:

s := make([]int, 0, 9)
var v int
fmt.Println(uint64(cap(s)) * uint64(unsafe.Sizeof(v)) +
	uint64(unsafe.Sizeof(s)))

A general solution would need to use reflection to understand the full type tree.

References

See the standard package unsafe.

Displaying the contents of a value of unknown type

Summary

a value can be shown with the 'v' verb in the standard fmt package as follows:

v := struct{
      s string
      i int
      f float64
}{"", 42, 3.14}
fmt.Printf("%%v:  %v\n", v)
fmt.Printf("%%+v: %+v\n", v)
fmt.Printf("%%#v: %#v\n", v)

This prints the value in three different forms. Firstly in the default, minimal form, then, using the '+' flag, with the field names as well and finally, using the '#' flag you get a Go-syntax representation of the value which includes the data type.

References

See the standard package fmt.

Displaying the type of a value

Summary

The type value can be shown with the 'T' verb in the standard fmt package as follows:

var v int
var i interface{} = v
fmt.Printf("%T\n", i)

This prints the type of a value. It's particularly useful when you are given an interface value where it will show the type of the actual value. It's also useful for debugging or error reporting. It doesn't take any flags.

References

See the standard package fmt.

Author: Nick Wells

Created: 2021-06-11 Fri 23:44

Validate