[Golang] Struct

2021-10-22 hit count image

Let's how to define Struct and how to use Struct in Golang.

Outline

In this blog post, I will show you how to use Struct to define variables and how to use it in Golang. You can see the full source code of this blog post on the link below.

Struct

In Golang, the struct is a type that binds various type of fields together.

Struct role

The struct makes code coupling and dependency low and cohesion high.

Low coupling, high cohesion

  • Function binds related code blocks, so it makes cohesion high and better reusability.
  • Array binds same type data, so it makes cohension high.
  • Struct bind related data, so it makes cohesion high and better reusability.

This struct becomes the base of OOP(Object Oriented Programming).

Define struct

In Golang, you can define the struct like the below.

type TYPE_NAME struct {
  Field_NAME TYPE
  ...
  Field_NAME TYPE
}

You can use the field name to make the public field or private field like Function. If the field name starts with an uppercase letter, it can be used as public field. If it starts wit a lowercase letter, it can be used as private field.

To check this, create the main.go file and modify it like the below.

package main

import "fmt"

type Student struct {
  Name  string
  Class int
  No    int
}

func main() {
  var s Student
  s.Name = "Tom"
  s.Class = 1
  s.No = 1

  fmt.Println(s)
  fmt.Printf("%v\n", s)
  fmt.Printf("Name: %s, Class: %d, No: %d\n", s.Name, s.Class, s.No)
}

When you execute the program, you can see the following result.

# go run main.go
{Tom 1 1}
{Tom 1 1}
Name: Tom, Class: 1, No: 1

Initialization

If you define a struct variable without initialization, all fields will be assigned the default value of the type.

type Student struct {
  Name  string
  Class int
  No    int
}

var s Student;

When you define the struct variable, you can assign intial values in field order like the below.

var s1 Student = Student{"Tom", 1, 2}
var s2 Student = Student{
  "John",
  1,
  3,
}

Also, you can assign the initial values with field names like the below.

var a Student = Student{ Name: "Deku", Class: 1, No: 3 };

To check these, modify the main.go file like the below.

package main

import "fmt"

type Student struct {
  Name  string
  Class int
  No    int
}

func main() {
  var s Student
  fmt.Println(s)

  var s1 Student = Student{"Tom", 1, 2}
  var s2 Student = Student{
    "John",
    1,
    3,
  }
  fmt.Println(s1)
  fmt.Println(s2)

  var s3 Student = Student{Name: "Deku", Class: 1, No: 3}
  fmt.Println(s3)
}

When you execute the code above, you can see the following result.

# go run main.go
{ 0 0}
{Tom 1 2}
{John 1 3}
{Deku 1 3}

Nested struct

In Golang, Struct can inlcude other structs. This is called Nested struct in Golang.

type ClassInfo struct {
  Class int
  No int
}

type Student struct {
  Class ClassInfo
  Name string
}

You can initialize the nested struct like the below.

var s Student = Student{
  Class: ClassInfo{Class: 1, No: 1},
  Name:  "John",
}

To check this, modify the main.go file.

package main

import "fmt"

type ClassInfo struct {
  Class int
  No    int
}

type Student struct {
  Class ClassInfo
  Name  string
}

func main() {
  var s Student = Student{
    Class: ClassInfo{Class: 1, No: 1},
    Name:  "John",
  }

  fmt.Println(s.Class.Class)
  fmt.Println(s.Class.No)
  fmt.Println(s.Name)
}

When you execute the code, you can see the following result.

# go run main.go
1
1
John

Embedded field

In Golang, Struct can include other structs, and you can omit the field names. This is called Embedded field in Golang.

type ClassInfo struct {
  Class int
  No int
}

type Student struct {
  ClassInfo
  Name string
}

You can initialize the embedded field like the below.

var s Student = Student{
  ClassInfo: ClassInfo{Class: 1, No: 1},
  Name:      "John",
}

And, you can directly acceess the embedded field to use it like the below.

fmt.Println(s.Class)
fmt.Println(s.No)
fmt.Println(s.Name)

Also, you can define the duplicated field name with embedded field.

type ClassInfo struct {
  Class int
  No int
}

type Student struct {
  ClassInfo
  Name string
  No int
}

In this case, you can initialize it like the below.

var s1 DupStudent = DupStudent{
  ClassInfo: ClassInfo{Class: 1, No: 1},
  Name:      "John",
  No:        10,
}

The field name is duplicated, so when you access the field directly, the value of the current struct variable is returned.

fmt.Println(s1.No) // 10

Therefore, if you want to access the value of the embedded field, you can access it like the below.

fmt.Println(s1.ClassInfo.No) // 1

To check these, modify the main.go file.

package main

import "fmt"

type ClassInfo struct {
  Class int
  No    int
}

type Student struct {
  ClassInfo
  Name string
}

type DupStudent struct {
  ClassInfo
  Name string
  No   int
}

func main() {
  var s Student = Student{
    ClassInfo: ClassInfo{Class: 1, No: 1},
    Name:      "John",
  }

  fmt.Println(s.Class)
  fmt.Println(s.No)
  fmt.Println(s.Name)

  var s1 DupStudent = DupStudent{
    ClassInfo: ClassInfo{Class: 1, No: 1},
    Name:      "John",
    No:        10,
  }

  fmt.Println(s1.Class)
  fmt.Println(s1.No)
  fmt.Println(s1.Name)
  fmt.Println(s1.ClassInfo.No)
}

When you execute the code after modifying, you can see the following result.

# go run main.go
1
1
John
1
10
John
1

Memory alignment

When you write the code in Golang, maybe you don’t need to care about the memory. But, if you write the code for the small device or for memory efficiency, you need to know Memory alignment.

s := Student{"John", 1}
var str string = "John"
var i int = 1

fmt.Println(unsafe.Sizeof(s))
fmt.Println(unsafe.Sizeof(str))
fmt.Println(unsafe.Sizeof(i))

To make the CPU calculate easily, Golang stores the Struct in memory in multiples of 8 bytes.

24
16
8

So, if the field type is smaller than 8 bytes, Golang adds empty spaces(Memory Padding) to make it multiples of 8 bytes.

type Memory struct {
  A int8 // 1 byte
  B int // 8 bytes
  C int8 // 1 byte
  D int // 8 bytes
  E int8 // 1 byte
  // 19 bytes
}

Like above, if we think about the memories of the typs, it will be 19 bytes. However, Struct will be stored in multiples 8 bytes, so Golang adds the empty spaces to A, C, E variables to amke them 8 bytes. So, the real memory size is 40 bytes.

To avoid the wasted memory by empty spaces(Memory Padding), you can do memory alignment by declaring a small memory first as the follows.

type Memory struct {
  A int8 // 1 byte
  C int8 // 1 byte
  E int8 // 1 byte
  B int // 8 bytes
  D int // 8 bytes
  // 19 bytes
}

This struct also uses 19 bytes, but A, C, E are defined first. So, these variables use 3 bytes, and then Golang adds 5 empty spaces to make it 8 bytes. So, the 20 bytes mermory is used to store this struct.

Like this, Golang stores the Struct in multiples of 8 bytes to make the CPU calculate easily. So, if you don’t waste the memory, you should think about the memory alignment.

To check these, open the main.go file and modify it like the below.

package main

import (
  "fmt"
  "unsafe"
)

type Student struct {
  Name  string
  Class int
}

type Memory struct {
  A int8
  B int
  C int8
  D int
  E int8
}

type MemoryAlignment struct {
  A int8
  C int8
  E int8
  B int
  D int
}

func main() {
  s := Student{"John", 1}
  var str string = "John"
  var i int = 1

  fmt.Println(unsafe.Sizeof(s))
  fmt.Println(unsafe.Sizeof(str))
  fmt.Println(unsafe.Sizeof(i))

  m := Memory{1, 2, 3, 4, 5}
  fmt.Println(unsafe.Sizeof(m))

  ma := MemoryAlignment{1, 2, 3, 4, 5}
  fmt.Println(unsafe.Sizeof(ma))
}

When you execute the code, you can get the result like the below.

# go run main.go
24
16
8
40
24

Completed

Done! we’ve seen how to define the Struct and how to use it. Also, we’ve seen the Struct memory usages, and how to use Memory Alignment to use the memory more efficiently.

Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!

Posts