This is part 1 of a 2 part blog series on Creating Objects from Types in Golang. This part discusses creation of primitive types.

Golang Gopher

The reflect package in Golang provides the necessary APIs to change the control flow of the program based on the type of the object being processed.

The reflect package provides two important structures - Type and Value .

Type is a representation of any Go type. i.e. It can be used to encode any Go type (eg. int , string , bool , myCustomType etc.). Value is a representation of any Go value. i.e. It can be used to encode and manipulate any Go value.

Types vs Kinds

Golang has a hidden, less known convention of making a distinction between type and kind . The distinction can be understood using an example. Consider this struct:

type example struct {

field1 type1

field2 type2

}

The type of an object of this struct would be example . The kind of that object would be struct . The kind can be considered as the type of a type .

All structs in Golang are of the same kind , but not the same type

Composite types like Pointer , Array , Slice , Map etc. make this distinction between type and kind .

In contrast, Primitive types like int , float , string etc. do not make the distinction between kind and type . i.e. The kind of an int variable is int . The type of an int variable is also int .

Creating Objects from Types

In order to create an object from a type signature, both the type and kind of the object are necessary. Henceforth, when I use the term ‘type signature’, I mean Golang’s reflect.Type object for that type.

Creating Primitive Objects from Primitive Types

Primitive Objects can be created from their type signatures by using their Zero values.

Zero value of a type is the value of the an uninitialized object of that type

Here’s a list of all the primitive types in Golang

Bool

Int

Int8

Int16

Int32

Int64

Uint

Uint8

Uint16

Uint32

Uint64

Uintptr

Float32

Float64

Complex64

Complex128

String

UnsafePointer

Using reflect.Zero function, objects of primitive types can be created

func CreatePrimitiveObjects(t reflect.Type) reflect.Value {

return reflect.Zero(t)

}

This will create an object of the desired type and return a reflect.Value object corresponding to the underlying Zero value. In order to work with that object, its value needs to be extracted.

The value of an object can be extracted using the appropriate method for each of the primitive types.

Extracting Integer values

There are 5 Integer types in Golang:

Int

Int8

Int16

Int32

Int64

Int type denotes Integers of the default size defined by the platform. The next 4 types are Integers of sizes(in bits) 8, 16, 32 and 64 respectively.

In order to extract each of the Integer types, the reflect.Value object representing the Integer needs to be converted to the appropriate type.

Here’s how int32 should be extracted:

// Extract Int32

func extractInt32(v reflect.Value) (int32, error) {

if reflect.Kind() != reflect.Int32 {

return int32(0), errors.New("Invalid input")

}

var intVal int64

intVal = v.Int()

return int32(intVal), nil

}

It is important to note that reflect.Int() always returns int64 . This is because int64 can encode all the other Integer types within it.

Here’s how the rest of the Integer types should be extracted:

// Extract Int64

func extractInt64(v reflect.Value) (int64, error) {

if reflect.Kind() != reflect.Int64 {

return int64(0), errors.New("Invalid input")

}

var intVal int64

intVal = v.Int()

return intVal, nil

} // Extract Int16

func extractInt16(v reflect.Value) (int16, error) {

if reflect.Kind() != reflect.Int16 {

return int16(0), errors.New("Invalid input")

}

var intVal int64

intVal = v.Int()

return int16(intVal), nil

} // Extract Int8

func extractInt8(v reflect.Value) (int8, error) {

if reflect.Kind() != reflect.Int8 {

return int8(0), errors.New("Invalid input")

}

var intVal int64

intVal = v.Int()

return int8(intVal), nil

} // Extract Int

func extractInt(v reflect.Value) (int, error) {

if reflect.Kind() != reflect.Int {

return int(0), errors.New("Invalid input")

}

var intVal int64

intVal = v.Int()

return int(intVal), nil

}

Extracting Boolean values

Boolean values are represented by the constant Bool in the reflect package.

They can be extracted from reflect.Value object using the Bool() method:

// Extract Bool

func extractBool(v reflect.Value) (bool, error) {

if reflect.Kind() != reflect.Bool {

return false, errors.New("Invalid input")

}

return v.Bool(), nil

}

Extracting Unsigned Integers

There are 5 Unsigned Integer types in Golang:

Uint

Uint8

Uint16

Uint32

Uint64

Uint type denotes Unsigned Integers of the default size defined by the platform. The next 4 types are Unsigned Integers of sizes(in bits) 8, 16, 32 and 64 respectively.

In order to extract each of the Unsigned Integer types, the reflect.Value object representing the Unsigned Integer needs to be converted to the appropriate type.

Here’s how Uint32 should be extracted:

// Extract Uint32

func extractUint32(v reflect.Value) (uint32, error) {

if reflect.Kind() != reflect.Uint32 {

return uint32(0), errors.New("Invalid input")

}

var uintVal uint64

uintVal = v.Uint()

return uint32(uintVal), nil

}

It is important to note that reflect.Uint() always returns uint64 . This is because uint64 can encode all the other Integer types within it.

Here’s how the rest of the Unsigned Integer types should be extracted

// Extract Uint64

func extractUint64(v reflect.Value) (uint64, error) {

if reflect.Kind() != reflect.Uint64 {

return uint64(0), errors.New("Invalid input")

}

var uintVal uint64

uintVal = v.Uint()

return uintVal, nil

} // Extract Uint16

func extractUint16(v reflect.Value) (uint16, error) {

if reflect.Kind() != reflect.Uint16 {

return uint16(0), errors.New("Invalid input")

}

var uintVal uint64

uintVal = v.Uint()

return uint16(uintVal), nil

} // Extract Uint8

func extractUint8(v reflect.Value) (uint8, error) {

if reflect.Kind() != reflect.Uint8 {

return uint8(0), errors.New("Invalid input")

}

var uintVal uint64

uintVal = v.Uint()

return uint8(uintVal), nil

} // Extract Uint

func extractUint(v reflect.Value) (uint, error) {

if reflect.Kind() != reflect.Uint {

return uint(0), errors.New("Invalid input")

}

var uintVal uint64

uintVal = v.Uint()

return uint(uintVal), nil

}

Extracting Floating Point Numbers

There are 2 Floating Point Number types in Golang:

Float32

Float64

Float32 type denotes Floating Point numbers that are 32 bits in size. Float64 type denotes Floating Point numbers that are 64 bits in size.

In order to extract each of the Floating Point Number types, the reflect.Value object representing the Floating Point Number needs to be converted to the appropriate type.

Here’s how Float32 should be extracted:

// Extract Float32

func extractFloat32(v reflect.Value) (float32, error) {

if reflect.Kind() != reflect.Float32 {

return float32(0), errors.New("Invalid input")

}

var floatVal float64

floatVal = v.Float()

return float32(floatVal), nil

}

It is important to note that reflect.Float() always returns float64 . This is because float64 can encode all the other Floating Point Number types within it.

Here’s how 64-bit Floating Point Number values should be extracted

// Extract Float64

func extractFloat64(v reflect.Value) (float64, error) {

if reflect.Kind() != reflect.Float64 {

return float64(0), errors.New("Invalid input")

}

var floatVal float64

floatVal = v.Float()

return floatVal, nil

}

Extracting Complex Values

There are 2 Complex types in Golang:

Complex64

Complex128

Complex64 type denotes Complex numbers that are 64bits in size. Complex128 type denotes Complex numbers that are 128 bits in size.

In order to extract each of the Complex types, the reflect.Value object representing the Complex value needs to be converted to the appropriate type.

Here’s how Complex64 should be extracted:

// Extract Complex64

func extractComplex64(v reflect.Value) (complex64, error) {

if reflect.Kind() != reflect.Complex64 {

return complex64(0), errors.New("Invalid input")

}

var complexVal complex128

complexVal = v.Complex()

return complex64(complexVal), nil

}

It is important to note that reflect.Complex() always returns complex128 . This is because complex128 can encode all the other Complex types within it.

Here’s how 128-bit Complex value should be extracted

// Extract Complex128

func extractComplex128(v reflect.Value) (complex128, error) {

if reflect.Kind() != reflect.Complex128 {

return complex128(0), errors.New("Invalid input")

}

var complexVal complex128

complexVal = v.Complex()

return complexVal, nil

}

Extracting string values

String values are represented by the constant String in the reflect package.

They can be extracted from reflect.Value object by using the String() method of the object.

Here’s how String should be extracted:

// Extract String

func extractString(v reflect.Value) (string, error) {

if reflect.Kind() != reflect.String {

return "", errors.New("Invalid input")

}

return v.String(), nil

}

Extracting Pointer values

There are 2 Pointer types in Golang:

Uintptr

UnsafePointer

Uintptr and UnsafePointer are just uint values representing a virtual address in the process memory. It can represent the location of a variable or a function.

The difference between Uintptr and UnsafePointer is that Uintptr is type-checked by the Go runtime, whereas UnsafePointer is not. UnsafePointer can be used to convert any Go type to any other Go type, given that their memory layouts are compatible. If this is something you would like to explore, please comment below, and I’ll write more on it.

Uintptr and UnsafePointer can be extracted from reflect.Value object by using the Addr() method and UnsafeAddr() method respectively. Here’s an example showing how Uintptr should be extracted

Uintptr should be extracted this way // Extract Uintptr

func extractUintptr(v reflect.Value) (uintptr, error) {

if reflect.Kind() != reflect.Uintptr {

return uintptr(0), errors.New("Invalid input")

}

var ptrVal uintptr

ptrVal = v.Addr()

return ptrVal, nil

}

Here’s how UnsafePointer value should be extracted

// Extract UnsafePointer

func extractUnsafePointer(v reflect.Value) (unsafe.Pointer, error) {

if reflect.Kind() != reflect.UnsafePointer {

return unsafe.Pointer(0), errors.New("Invalid input")

}

var unsafeVal unsafe.Pointer

unsafeVal = unsafe.Pointer(v.UnsafeAddr())

return unsafeVal, nil

}

It is important to note that v.UnsafeAddr() above returns a uintptr value. It should be type converted in the same line, otherwise the unsafe.Pointer value may not be pointing at the intended location.

What’s next

Please note that none of the methods of the reflect.Value struct should be used without the check for their kind , since it can lead to a panic easily.