## mdawar.dev

A blog about programming, Web development, Open Source, Linux and DevOps.

# Go - Structs

## Struct Definition

• A `struct` is a composite data type that groups named values together as a single entity
• Each value in a `struct` is called a field
• A field is exported if it begins with a capital letter
• The `struct` fields can be of any data type
go
``````// A struct is a sequence of named elements called fields.
type Person struct {
// Each field has a name and a type.
Name string

// The field is exported if it begins with a capital letter.
LastName string

// Unexported field (accessible only in the declaring package).
salary int

// Fields of the same type may be declared separated by commas.
// Usually related fields of the same type are combined.
Weight, Height float64

IsEmployed bool
}``````

## Struct Zero Value

The zero value for a struct is composed of the zero values of each of its fields.

go
``````var person Person

person.Name       // ""
person.LastName   // ""
person.salary     // 0 (Accessible only in the declaring package)
person.Weight     // 0
person.Height     // 0
person.IsEmployed // false``````

## Struct Instance

go
``````type Point struct {
X, Y int
}

// Create an instance of the `Point` struct.
// The struct fields will be initialized to their zero values.
var p1 Point // Point{X:0, Y:0}

// Create an instance using the struct literal syntax
// with all the fields initialized to their zero values.
p2 := Point{} // Point{X:0, Y:0}

// Create an instance of a struct with initial field values
// Set the fields by listing their names and values.
p3 := Point{X: 1, Y: 2} // Point{X:1, Y:2}

// The order of the fields does not matter when specifying their names.
p4 := Point{Y: 2, X: 1} // Point{X:1, Y:2}

// Omitted fields are set to their zero values.
p5 := Point{Y: 3} // Point{X:0, Y:3}

// Set the field values using their order.
// In this form the values must be set for every field in the right order.
// This form is used with small struct types like this one.
p6 := Point{4, 5} // Point{X:4, Y:5}``````

## Pointer to Struct Instance

go
``````type Point struct {
X, Y int
}

// Using the address operator with a struct instance.
var point Point
p1 := &point // *Point

// Using the address operator with a struct literal syntax.
p2 := &Point{} // *Point

// Using the `new` function.
p3 := new(Point) // *Point``````

## Struct Fields

Fields are accessed using dot notations.

go
``````type Point struct {
X, Y int
}

var point Point

// Get the value of a field.
x := point.X

// Set the value of a field.
point.X = 5

// A pointer to the struct.
var p *Point = &point

// Set a field value using the struct pointer.
// Struct fields can be accessed through a struct pointer.
// Explicit dereferencing is not required.
p.X = 7

// Equivalent to
(*p).X = 7

// A pointer to a struct field.
y := &point.Y // *int

// Set the field value using a pointer.
*y = 10``````

## Struct Methods

Methods can be defined on structs using receiver functions.

go
``````type Person struct {
Name string
}

// Define a method on the `Person` struct.
func (p Person) Greet() {
fmt.Printf("Hello, my name is %s", p.Name)
}

person := Person{"John"}

// Call a method on a struct instance.
person.Greet()``````

## Struct Embedding

• Struct embedding is used for code reuse and as a form of composition
• A field declared with a type without an explicit field name is called an embedded field or an anonymous field
go
``````type inner struct {
C int
}

type Central struct {
B int

// `inner` is embedded within Central.
// The field name is `inner` (Unexported, starts with a lowercase letter).
// Accessible in the declaring package only.
inner
}

// A method defined on the `Central` type.
func (e Central) Method() error {
return nil
}

type Outer struct {
A int

// `Central` is embedded within `Outer`.
// The field name is `Central` (exported, starts with an uppercase letter).
Central
}

// The struct literal syntax must follow the shape of the type delcaration.
outer := Outer{
A: 1,

Central: Central{
B: 2,

inner: inner{
C: 3,
},
},
}

// Struct literal syntax using the fields order.
outer = Outer{1, Central{2, inner{3}}}

// Non embedded field.
outer.A

// The fields and methods of the embedded types will be available on the outer struct.
// The anonymous fields can be omitted when referring to their subfields and methods,
// they are called promoted fields and methods.
outer.B        // outer.Central.B
outer.C        // outer.Central.inner.C
outer.Method() // outer.Central.Method()

// The embedded fields and methods can be accessed explicitly.
outer.Central.B
outer.Central.Method()
outer.Central.inner.C // Accessible only in the declaring package
outer.inner.C         // Accessible only in the declaring package``````

### Embedded Field Name

An anonymous field can be defined with any named type or a pointer to a named type and not only `struct` types.

The name and visibility of the anonymous field are implicitly determined by the type name.

go
``````type Example struct {
// Anonymous field defined with a named type.
T1 // field name: T1 (Exported field, starts with an uppercase letter)

// Anonymous field defined with a pointer to a named type.
*T2 // field name: T2

// The unqualified type name acts as the field name.
P.T3  // field name: T3
*P.T4 // field name: T4

// The first letter casing determines the field's visibility.
t5 // field name: t5 (Unexported field, starts with a lowercase letter)

*T1   // illegal: field names must be unique (Conflicts with T1)
P.T2  // illegal: field names must be unique (Conflicts with T2)
}``````

### Embedded Field Name Conflicts

If the embedded type has fields or methods with conflicting names, the outer struct’s fields and methods take precedence when using the shorthand notation.

go
``````type inner struct {
A, B, C int
}

type Central struct {
A, B int

inner
}

type Outer struct {
A int

Central
}

outer := Outer{1, Central{2, 3, inner{4, 5, 6}}}

outer.A         // 1 (Outer.A takes precedence)
outer.Central.A // 2 (Central.A takes precedence)
outer.B         // 3 (Central.B takes precedence)
outer.inner.A   // 4 (Or: outer.Central.inner.A)
outer.inner.B   // 5 (Or: outer.Central.inner.B)
outer.C         // 6 (No conflicts)``````

## Struct Arguments and Return Values

go
``````type Point struct { X, Y int }

// Struct values can be passed to and returned from functions.
func Scale(p Point, factor int) Point {
return Point{p.X * factor, p.Y * factor}
}

// In Go, functions receive a copy of the argument.
// If a function needs to modify a struct value then a pointer must be passed.
func Reset(p *Point) {
// The fields can be accessed without explicit pointer dereferencing.
p.X = 0
p.Y = 0
}

// Even if the function does not need to modify the struct
// a pointer may be used with large structs for efficiency
// to avoid copying the struct value.
func Calculate(s *LargeData) int {
// ...
}

// The same can be used to return a large struct from a function.
func CreateLargeData() *LargeData {
return &LargeData{
// ...
}
}``````

## Comparing Structs

• A `struct` is comparable if all of its fields are comparable
• The `==` and `!=` operations compare the fields of the structs in order
• Comparable `struct` types may be used as the key type of a map
go
``````type Point struct { X, Y int }

var p1 Point  // Point{X:0, Y:0}

p2 := Point{} // Point{X:0, Y:0}

// Comparing structs compares their fields in order.
fmt.Println(p1 == p2) // true

// Same as comparing the struct fields one by one.
fmt.Println(p1.X == p2.X && p1.Y == p2.Y) // true``````

## Field Tags

A field declaration may be followed by an optional string literal called a tag which becomes an attribute for the field or multiple fields if they are combined in one declaration.

go
``````type FieldTags struct {
// A field declaration may be followed by an optinal tag (string literal).
// The tag becomes an attribute for the field.
example string "Any string can be used as a tag"

// The tag becomes an attribute for all the fields in the same declaration.
a, b int "Tag for both a and b"

// An empty tag is like an absent tag.
x float64 ""

// By convention, tag strings are a concatenation of optionally space-separated
// key:"value" pairs in a raw string literal (between back quotes).
F string `key:"value" color:"blue"`
}``````

Tags can be accessed using the `reflect` standard library package otherwise they are ignored.

go
``````package main

import (
"fmt"
"reflect"
)

func main() {
type Tags struct {
TaggedField string `key:"value" color:"blue"`
}

s := Tags{}

st := reflect.TypeOf(s)

// Get the first field in the struct (panics on errors).
field := st.Field(0)

fmt.Println(field.Tag.Get("key"))   // "value"
fmt.Println(field.Tag.Get("color")) // "color"

// Or get the field by name.
if field, ok := st.FieldByName("TaggedField"); !ok {
} else {
fmt.Println(field.Tag.Get("key"))   // "value"
fmt.Println(field.Tag.Get("color")) // "color"
}
}``````

## Empty Struct

go
``````// Empty struct with no fields (has size zero).
struct{}

// When creating a map representing a set, the empty struct may be used
// instead of bool as the value type to save space.
// Note: the space saving is not significant.
set := make(map[string]struct{}) // Instead of map[string]bool

if _, ok := set["value"]; !ok {
set["value"] = struct{}{}
}``````

## Recursive Data Structures

A named `struct` type cannot declare a field of the same type.

To create recursive data structures, a `struct` can declare a field of a pointer type.

go
``````type tree struct {
value       int
left, right *tree
}``````

## Field Order

The order of the `struct` fields can affect memory usage.

To save memory and improve performace, the `struct` fields can be ordered by their type size in descending order which can make the `struct` more efficient.

go
``````type Efficient struct {
A int64 // 8 bytes
B int32 // 4 bytes
C int16 // 2 bytes
D int8  // 1 byte
E bool  // 1 byte
}``````

The `fieldalignment` analyzer part of staticcheck can be used to detect structs that would use less memory if their fields were sorted.