Fundamentals 13 min read

Unlocking Go’s Type System: From Basic Types to Reflection

This article explains Go’s static type system, introduces custom types and interface types, demonstrates how empty interfaces work, and shows how the reflect package can inspect and modify values at runtime, including struct fields, using concrete code examples.

Raymond Ops
Raymond Ops
Raymond Ops
Unlocking Go’s Type System: From Basic Types to Reflection

Types and Interfaces

Go is a statically typed language; each variable has a static type known at compile time, such as int, float32, or a user‑defined type.

type MyInt int
var i int
var j MyInt

i has type int, j has type MyInt; they cannot be assigned to each other without conversion.

Interface types are collections of method signatures. Any value that implements those methods can be stored in an interface variable. For example, io.Reader and io.Writer are defined in the io package.

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

An io.Reader variable can hold any value that implements Read, such as os.Stdin, bufio.NewReader, or a bytes.Buffer. The variable’s static type remains io.Reader.

The empty interface interface{} has no methods and can hold a value of any type.

Representing Interfaces

Russ Cox’s blog explains that an interface value stores a pair: the concrete value and its type description.

var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)

Type assertions can extract the concrete type from an interface value:

var w io.Writer
w = r.(io.Writer)

Assigning a concrete value to an empty interface preserves both the value and its full type information.

From Interface to Reflect Value

The reflect package provides reflect.TypeOf and reflect.ValueOf to inspect the type and value stored in an interface.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var f float64 = 13.4
    fmt.Println(reflect.TypeOf(f))
    fmt.Println("Hello, playground")
}

Output shows the type float64. reflect.ValueOf returns a reflect.Value that offers methods such as Kind, Int, Float, and SetFloat.

var f float64 = 13.44444
v := reflect.ValueOf(f)
fmt.Println(v)          // 13.44444...
fmt.Println(v.Type())   // float64
fmt.Println(v.Kind())   // float64
fmt.Println(v.Float())  // 13.44444...

Only addressable values are settable; CanSet reports whether a reflect.Value can be modified. To modify a variable via reflection you must obtain a pointer and call Elem() to get the underlying settable value.

var x float64 = 3.4
p := reflect.ValueOf(&x)   // pointer to x
v := p.Elem()              // settable reflect.Value
fmt.Println(v.CanSet())   // true
v.SetFloat(7.1)
fmt.Println(x)            // 7.1

Modifying Struct Fields

Reflection can also change exported struct fields when you have a pointer to the struct.

type T struct {
    A int
    B string
}

t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem()
s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now", t)

The output is {77 Sunset Strip}. Only exported fields are settable.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

programmingReflectiontypesstatic typingInterfaces
Raymond Ops
Written by

Raymond Ops

Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.