Value Parts

The articles following the current one will introduce more kinds of Go types. To easily and deeply understand those articles, it is best to read the following content in the current article firstly before reading those articles. In other words, we should know there are two categories of Go types.

Two Categories Of Go Types

Go is a C-family language, which can be confirmed from the two previous articles structs in Go and pointers in Go. Struct types and pointer types in Go and C are much alike. The main characteristic of C types is the memory layouts of their values are transparent. Each C value in memory occupies one continuous memory segment. Below, such a continuous memory segment will be called a value part.

On the other hand, Go can also be viewed as C language framework. This is mainly reflected in the fact that Go supports several kinds of types whose value memory layouts are not totally transparent. Each values of the these kinds of types is often composed of one direct part and one or several underlying indirect parts, and the underlying value parts are referenced by the direct value part. The following picture depicts a multi-part value.

The above two paragraphs describe two categories of Go types. Types in the two categories are listed in the following two paragraphs.

Up to now, except string types, all other kinds of types introduced in the previous artciles are C alike. These types include boolean types, numeric types, struct types and pointer types. The other two kinds of C alike types are array types and unsafe pointer types. Array types will be explained in the next article and unsafe pointer types will be explained in the article the unsafe standard package later. Each C alike value is only composed of one direct value part.

Values of all other kinds of types not mentioned in the last paragraph may contain underlying parts. Specifically speaking, these kinds of types include

These kinds of types bring much convenience to Go programming by encapsulating many implementation details. Different Go compilers may adopt different internal implementations for these types, but the external behaviors of values of these types must satisfy the requirements specified in Go specification. These types will be explained detailedly in the following articles.

The types in the second category are not fundamental types for a language, we can implement them from scratch by using the types in the first category. However, the implementations would be quite verbose and sometimes boring. By encapsulating some common or unique functionalities and supporting these types as the first-level citizens in Go, the experiences of Go programming become enjoyable and productive.

On the other hand, these encapsulations of implementations of the types in the second category hide many internal definitions of these types. This prevents Go programmers from viewing the whole pictures of these types, and sometimes makes some obstacles to understanding Go better.

To help gophers better understand the types in the second category and their values, the following content of this article will introduce the internal structure definitions of these kinds of types. The detailed implementations of these types will not be explained here. The explanations in this article are based on, but not exactly the same as, the implementations used by the standard Go compiler.

Two Kinds Of Pointer Types In Go

Before showing the internal structure definitions of the kinds of types in the second category, let's clarify more on pointers and references.

We have learned Go pointers in the last article. The pointer types in that article are type-safe pointer types. In fact, Go also supports type-unsafe pointer types. The unsafe.Pointer type provided in the unsafe standard package is like void* in C language. Types whose underlying types are unsafe.Pointer are called unsafe pointer types in Go 101.

In other articles in Go 101, if not special specified, when a pointer type is mentioned, it means a type-safe pointer type. However, in the following parts of the current article, when a pointer type is mentioned, it may be either a type-safe pointer type or a type-unsafe pointer type.

A pointer value stores a memory address of another value, unless the pointer value is a nil pointer. We can say the pointer value references the other value, or the other value is referenced by the pointer value. Values can also be referenced indirectly.

A struct type with fields of pointer types can be called a pointer wrapper type. A struct type with fields of pointer wrapper type call also be called a pointer wrapper type.

Internal Definitions Of The Types In The Second Category

The internal definitions of the types in the second category are shown below.

Internal Definitions Of Map, Channel And Function Types

The internal definitions of map, channel and function types are similar:
// map types
type _map *hashtableImpl // currently, for the standard Go compiler,
                         // Go maps are hashtables actually.

// channel types
type _channel *channelImpl

// function types
type _function *functionImpl

So, internally, types of the three families are just pointer types. The zero values of these types are all represented by the predeclared nil identifier. For any non-nil value of these types, its direct part (a pointer) references its indirect underlying implementation part.

In fact, the indirect underlying part of a value of the three kinds of types may also reference deeper indirect underlying parts. But the current article will ignore the deeper ones and first level indirect ones.

Internal Definition Of Slice Types

The internal definition of slice types is like:
// slice types
type _slice struct {
	elements unsafe.Pointer // underlying elements
	len      int            // number of elements
	cap      int            // capacity
}

So, internally, slice types are pointer wrapper struct types. The zero values of slice types are also represented by the predeclared nil identifier. Each non-nil slice value has an indirect underlying part which stores the element values of the slice value. The elements field of the direct part references the indirect underlying part of the slice value.

Internal Definition Of String Types

Below is the internal definition for string types:
// string types
type _string struct {
	elements *byte // underlying bytes
	len      int   // number of bytes
}

So string types are also pointer wrapper struct types internally. The zero values of string types are blank strings, which are represented by "" or `` in literal. Each string value has an indirect underlying part storing the bytes of the string value, the indirect part is referenced by the elements field of that string value.

Internal Definition Of Interface Types

Below is the internal definition for general interface types:
// general interface types
type _interface struct {
	dynamicType  *_type         // the dynamic type
	dynamicValue unsafe.Pointer // the dynamic value
}

Internally, interface types are also pointer wrapper struct types. The internal definition of an interface type has two pointer fields. The zero values of interface types are also represented by the predeclared nil identifier. Each non-nil interface value has two indirect underlying parts which store the dynamic type and dynamic value of that interface value. The two indirect parts are referenced by the dynamicType and dynamicValue fields of that interface value.

In fact, for the standard Go compiler, the above definition is only used for blank interface types. Blank interface types are the interface types which don't specify any methods. We can learn more about interfaces in the article interfaces in Go later. For non-blank interface types, a definition like the following one is used.
// general interface types
type _interface struct {
	dynamicTypeInfo *struct {
		dynamicType *_type       // the dynamic type
		methods     []*_function // implemented methods
	}
	dynamicValue unsafe.Pointer // the dynamic value
}

The methods field of the dynamicTypeInfo field of an interface value stores the implemented methods of the dynamic type of the interface value for the (interface) type of the interface value.

Go runtime will create at most one dynamic type information struct for each interface type and dynamic type pair and store the struct value in the global zone. For the standard Go runtime, a dynamic type information struct value will be never garbaged collected once it is created. All _type values are also stored in the global zone. In other words, the _type values and dynamic type information struct values are shared by many interface values, they don't belong to any specified interface value. For this reason, the current article will view each interface value may has at most one underlying value, which is referenced by the dynamicValue field.

Underlying Value Parts Are Not Copied In Value Assignments

Now we have learned that the internal definitions of the types in the second category are either pointer types or struct types. Certainly, Go compilers will never view the types in the second category as pointer types or struct types in user programs. These definitions are just used internally by Go runtimes.

In Go, each value assignment (including parameter passing, etc) is a shallow value copy if the involved destination and source values have the same type (if their types are different, we can think that the source value will be implicitly converted to the destination type before doing that assignment). In other words, only the direct part of the source value is copied to the destination value in an value assignment. If the source value has underlying value part(s), then the direct parts of the destination and source values will reference the same underlying value part(s), in other words, the destination and source values will share the same underlying value part(s).

In fact, the above descriptions are not 100% correct in theory, for strings and interfaces. The official Go FAQ says the underlying dynamic value part of an interface value should be copied as well when the interface value is copied. However, as the dynamic value of an interface value is read only, the standard Go compiler/runtime doesn't copy the underlying dynamic value parts in copying interface values. This can be viewed as a compiler optimization. The same situation is for string values and the same optimization (made by the standard Go compiler/runtime) is made for copying string values. So, for the standard Go compiler/runtime, the descriptions in the last section are 100% correct, for values of any type.

Since an indirect underlying part may not belong to any value exclusively, it doesn't contribute to the size returned by the unsafe.Sizeof function.

As above mentioned, Go map values are hashtables internally. When an interface or a string value is used as a map key, its underlying part(s) are counted in calculating its hash. When a channel is used as a map key, its underlying part(s) are not counted in calculating its hash. Slice, map and function values can't be used as map keys.

About The "Reference Type" And "Reference Value" Terminologies

The word reference in Go world is a big mess. It brings many confusions to Go community. Some articles, including some official ones, use reference as qualifiers of types and values, or treat reference as the opposite of value. This is strongly not recommended in Go 101. I really don't want to dispute on this point. Here I just list some absolutely misuses of reference:

I don't mean the reference type or reference value terminologies are totally useless for Go, I just think they are not very essential. If we do need these terminologies, I prefer to define them as pointers and pointer wrappers. However, my personal opinion is it is best to limit the reference word to only representing relations, and never use it as qualifiers. This will avoid many confusions in leaning, teaching and using Go.


The Go 101 project is hosted on both github and gitlab. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.

Support Go 101 by playing Tapir's games. Cryptocurrency donations are also welcome:
Bitcoin: 1xucQbv5jujFPPwhyg395ri5yV71hx9g9
Ethereum: 0x5dc4aa2c2bbfaadae373dadcfca11b3358912212