본문 바로가기
Go

[Go] Structure

by llHoYall 2021. 10. 11.

Structure consists of different types of values.

The syntax is following:

type STRUCT_NAME struct {
  FIELD_NAME DATA_TYPE
  ...
}

If the name of a structure starts with a capital letter, it is exported to the outside of the package. 

Structure Declaration

var testStruct struct {
  numField  float64
  strField  string
  boolField bool
}
fmt.Println(testStruct)
// {0  false}]

testStruct.numField = 7
testStruct.strField = "Hi"
testStruct.boolField = true
fmt.Println(testStruct)
// {7 Hi true}

The zero value depends on the type of the field.

If you access the field in the Structure, you should use a dot(.).

Type Definition

You can make user-defined types using type definition.

package main

import "fmt"

type student struct {
  name  string
  score int
}

func main() {
  var hoya student
  hoya.name = "hoya"
  hoya.score = 98
  fmt.Println(hoya)
  // {hoya 98}
  
  var park student
  park.name = "park"
  park.score = 87
  fmt.Println(park)
  // {park 87}
}

You can also use Structure as a parameter type or return type of the function.

Structure Literal

hoya := student{"hoya", 98}
fmt.Println(hoya)
// {hoya 98}

park := student{name: "park"}
fmt.Println(park)
// {park 0}

kim := student{score: 97}
fmt.Println(kim)
// { 97}

You can omit some fields, and it will be filled with initial values of its type.

Structure with Pointer

This example shows you how to use a pointer variable with Structure.

var hoya student
hoya.name = "hoya"
hoya.score = 98

var p *student = &hoya
fmt.Println((*p).name, p.score)
// hoya 98

 

Go is a pass-by-value language.

So you need to use a pointer if you want to change the argument's value inside the function.

func addScore(st *student) {
  st.score++
}

func main() {
  var hoya student
  hoya.name = "hoya"
  hoya.score = 90
  addScore(&hoya)
  fmt.Println(hoya)
}
// {hoya 91}

Structure Nesting

Structure can be nested.

type point struct {
  x int
  y int
}

type worldMap struct {
  objA point
  objB point
}

func main() {
  locationA := point{x: 5, y: 7}
  locationB := point{x: 3, y: 6}
  
  currentMap := worldMap{objA: locationA, objB: locationB} 
  fmt.Println(currentMap)
  // {{5 7} {3 6}}
  
  currentMap.objA.y = 9
  currentMap.objB.x = 1
  fmt.Println(currentMap)
  // {{5 9} {1 6}}
}

Anonymous Field

Anonymous field means a field in a structure that does not have a name but only a type.

type point struct {
  x int
  y int
}

type shop struct {
  name string
  point
}

func main() {
  barberShop := shop{name: "pop's barber shop"}
  barberShop.point.x = 3
  barberShop.point.y = 5
  fmt.Println(barberShop)
  // {pop's barber shop {3 5}}
  
  barberShop.x = 7
  barberShop.y = 9
  fmt.Println(barberShop)
  // {pop's barber shop {7 9}}
}

In the first case, we can access the anonymous field with the name of the type.

It is said that the internal Structure declared as an anonymous field of the external Structure has been embedded into the external Structure.

The fields of embedded Structure is promoted to the external Structure.

In other words, we can access the fields of internal Structure like the fields of external Structure.

But of course, the field name should be unique.

Memory Alignment

Go's structure has padding for memory alignment the same as C.

	var struct1 struct {
	  field1 int64
	  field2 float64
	}
	var struct2 struct {
	  field1 int32
	  field2 float64
	}

	fmt.Println(unsafe.Sizeof(struct1)) // 16
	fmt.Println(unsafe.Sizeof(struct2)) // 16

The actual size is 16 and 12, but the result was both 16.

Because my machine is a 64 bits machine, so the memory bus width is 8 bytes and it is aligned by 8 bytes.

Therefore, the int32 field is added 4 bytes padding.

If my machine is a 32 bits machine, the result would be 16 and 12.

Encapsulation

Encapsulation is used when we protect the fields of the structure.

Let's make our own Date package.

 

This file should be located in the date folder of the workspace of Go.

 

date/date.go

package date

// Date structure
type Date struct {
  year  int
  month int
  day   int
}

Setter Method

Packages that want to set the protected fields of the structure has to use the setter method.

func (d *Date) SetYear(year int) error {
  if year < 1 {
    return errors.New("invalid year")
  }
  d.year = year
  return nil
}

func (d *Date) SetMonth(month int) error {
  if month < 1 || month > 12 {
    return errors.New("invalid month")
  }
  d.month = month
  return nil
}

func (d *Date) SetDay(day int) error {
  if day < 1 || day > 31 {
    return errors.New("invalid day")
  }
  d.day = day
  return nil
}

Getter Method

Packages that want to access the protected fields of the structure has to use the getter method.

func (d *Date) Year() int {
  return d.year
}

func (d *Date) Month() int {
  return d.month
}

func (d *Date) Day() int {
  return d.day
}

Usage Getter and Setter

You can't access the fields of the Date structure.

package main

import (
  "date"
  "fmt"
  "log"
)

func main() {
  today := date.Date{}
  err := today.SetYear(2020)
  if err != nil {
    log.Fatal(err)
  }
  err = today.SetMonth(11)
  if err != nil {
    log.Fatal(err)
  }
  err = today.SetDay(11)
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println(today.Year(), today.Month(), today.Day())
  // 2020 11 11
}

Encapsulation

Like the above example, hiding data in one area of a program from another code is called encapsulation.

Go usually exposes the field to the outside and approaches it directly.

However, encapsulation is used when validation of values in a structure field is required.

Embedding

We can embed other types in the structure types.

Let's make two files.

 

embedding/embedded.go

package embedding

// Embedded structure.
type Embedded struct {
  value int
  State bool
}

func (e *Embedded) SetValue(value int) {
  e.value = value
}

 

embedding/container.go

package embedding

// Container structure.
type Container struct {
  name string
  Embedded
}

func (c *Container) SetName(name string) {
  c.name = name
}

 

In the above example, the Container structure contains the Embedded structure.

Now, use these structures.

 

test.go

package main

import (
  "embedding"
  "fmt"
)

func main() {
  test := embedding.Container{}
  // test.name = "Container"    // Error
  test.Embedded.State = true
  // test.Embedded.value = 1    // Error
  fmt.Println(test)
  // { {0 true}}
  
  test.SetName("container")
  test.Embedded.SetValue(2)
  fmt.Println(test)
  // {container {2 true}}

  test.State = false
  test.SetValue(3)
  fmt.Println(test)
  // {container {3 false}}
}

First, you can access only exposed fields of a structure.

Second, you can access exposed embedded methods.

Third, exposed embedded methods can be promoted.

'Go' 카테고리의 다른 글

[Go] Rune and String  (0) 2021.10.16
[Go] Pointer  (0) 2021.10.11
[Go] Array  (0) 2021.10.10
[Go] Control Flow  (0) 2021.10.10
[Go] Constant  (0) 2021.10.05

댓글