Yeah, it's not going to be easy

Search

Search IconIcon to open search

Tucker's Golang programming Ch12 ~ Ch17

Oct 14, 2023

This post is a translation of the original Korean post.


This text is a summary of chapters 12 to 17 of the book “Golden Rabbit: [ Tucker’s Golang programming ]”.

Ch12 ~ Ch17

  • Array
  • Structure
  • Pointer
  • String
  • Packages
  • Create a number matching game

# Array

A data structure with multiple values of the same type

1
2
3
4
var variableName [number of elements]type

// Initialize by omitting the number of elements
x := [...]int{10, 20, 30}

When declaring an array, the number of elements is always a constant

range iteration

1
2
3
for i, v := range t { // i: index, v: raw value
	...
}

Continuous memory

  • Element position
    • Array start address + (index x type size)

copy array

Assignment operator:

Copies the value on the right-hand side into the memory space on the left-hand side

  • Copy by the size of the type (must be of the same type)

# Structure

A type that bundles multiple fields of different types

1
2
3
4
type typeName struct {
  fieldName type
  ...
}

Increased cohesion

  • Group related data together
  • Increases reusability

Initializes structs

  • Omit initialization
    • All fields are initialized to default values (zero value)
  • Initialize some fields
    • Rest of the fields are initialized with default values

Copy Structure Values

All field values in the structure are copied

# Structures containing structs

Can contain other structures as fields, just like regular types

field name omitted (included fields)

  • Child structures (struct fields) are directly accessible through parent structure variables
  • Overlapping field names in parent and child structures
    • Use fields from the parent structure
    • fields in the child structure are accessed using the child structure name

# Memory padding

register size

Size that can be used for a single operation

  • 32-bit computers: 4 bytes
  • 64-bit computers: 8 bytes

Spacing between fields for memory alignment

  • Align the number of bytes and starting address of the variable to a multiple (e.g. 4-byte variable: starting address that is a multiple of 4)
  • Variables of type 8 bytes on 64-bit computers
    • Since the register size is 8 bytes, it is a performance penalty if it is not allocated at an address that is a multiple of 8

# pointer

Types whose value is a memory address

  • Memory Addresses
    • Can be imported by prefixing regular variables with &

# Pointer variables

Variables that can have a memory address as their variable value

  • 8 bytes in size on a 64-bit computer

Declarations

1
var p *int

Accessing

Access pointer variables by prefixing them with *

1
*p = 20

nil

Indicates that no memory space is being pointed to

new() built-in function

allocates a type into memory, fills it with default values, and returns its address

# Escape analysis

Allocate memory by checking whether the memory space is public outside the function

Instances that are visible outside of the function (returning a memory address)

  • Does not disappear when function terminates
  • Allocate to heap memory, not stack.

# String

Set of characters

Expressions

  • Double quotes ("): special character behavior
  • Backquote ( ` ): treats special characters as regular characters
    • Can write strings across multiple lines

rune type

  • Represents a single character
  • Alias type of int32 type (4 bytes)
  • Can output a single character using the %c format

len()

  • a string
    • Returns the amount of memory (in bytes) occupied by the string
  • slice
    • Returns the number of elements in an array

# Type conversions

  • []rune
    • an array of individual characters
  • []byte
    • Array of 1 bytes

# String iteration

  • Byte-by-byte iteration: use indexes
    1
    2
    3
    
    for i := 0; i < len(str); i++ {
      str[i]
    }
    
  • Single-letter iteration
    • Using the []rune type
      1
      2
      3
      4
      
      arr := []rune(str)
      for i := 0; i < len(arr); i++ {
        arr[i]
      }
      
    • Using the range keyword
      1
      2
      3
      
      for _, v := range str {
        v // rune type
      }
      

# string structure

  • Data: uintptr (8 bytes)
    • A pointer to the memory address of the string
  • Len: int (8 bytes)
    • Length of the string

# Immutability

Difficult to track without immutability because we’re pointing to the string via a pointer

  • Global change is possible
    • Data and Len values change
  • Cannot be partially changed through indexes
  • Copying a string to a new memory space
    • When casting to a slice type
    • When summing strings
      • Recommend using strings.builder for frequent use.

# package

Largest unit to bundle code into

main package

  • Package containing the main() function (program start point)

# Installing packages

  • Packages provided by the go language
    • Found in the go installation path
  • Packages stored in external storage
    • Downloaded from external storage to match the version defined in go modules
  • Packages located under the current module
    • Look for packages under the current folder

# Initializing packages

  1. import the package
  2. initialize global variables in the package
  3. call the init() function in the package
    • init() function conditions: a function with no input parameters and no return value.

# Create a number matching game

Guess a randomly selected number from the program

  1. run the program
  2. enter the expected number in the console
  3. Exit the program if the answer is correct
  4. If it’s less than or equal to, print a message
  5. View the message and repeat from step 2
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package main  
  
import (  
    "bufio"  
    "errors"
    "fmt"
    "os"
    "sync"
    "time"
  
    "math/rand"
)
  
const (  
    smaller = iota + 1  
    larger
	equal
  
    inputPrint = "Please enter a numeric value"
  
    smallerPrint = "The number you entered is smaller."
    largerPrint  = "The number you entered is larger."
    equalPrint   = "You matched the number, congratulations!"
    countPrint   = "Number of attempts"
)  
  
var wg sync.WaitGroup  
  
func main() {  
    resultCh := make(chan game)  
  
    go func() {  
       defer close(resultCh)  
       err := newGame(resultCh)  
       if err != nil {  
          panic(err)  
       }  
    }()  
  
    printResult(resultCh)  
}  
  
type game struct {
	num    int   // Number generated 
	result uint8 // game result 
	cnt    int   // number of attempts  
}

// newGame Create and run the game  
func newGame(resultCh chan game) error {  
    g := &game{  
       num: newNum(),  
    }  
    stdin := bufio.NewReader(os.Stdin)  
    for {  
       wg.Wait() // wait for output
       if err := playGame(stdin, g); err != nil {  
          return err  
       }  
       resultCh <- *g  
       wg.Add(1)  
       if g.result == equal {  
          break  
       }  
    }  
    return nil  
}  
  
// printResult Print the result of the game  
func printResult(gameCh chan game) {  
    for {  
       g, ok := <-gameCh  
       if !ok {  
          break  
       }  
       switch g.result {  
       case smaller:  
          fmt.Println(smallerPrint)  
       case larger:  
          fmt.Println(largerPrint)  
       case equal:  
          fmt.Printf("%s %s: %d", equalPrint, countPrint, g.cnt)  
       }  
       wg.Done()  
    }  
}  
  
// playGame Take user input and see the result  
func playGame(stdin *bufio.Reader, g *game) error {  
    fmt.Printf("%s:", inputPrint)  
    num, err := inputNum(stdin)  
    if err != nil {  
       return err  
    }  
    g.cnt = g.cnt + 1  
    g.result = g.checkNum(num)  
    return nil  
}  
  
// checkNum Compare the number in the game with the number entered  
func (g *game) checkNum(num int) (result uint8) {  
    switch {  
    case g.num > num:  
       result = smaller  
    case g.num < num:  
       result = larger  
    case g.num == num:  
       result = equal  
    }  
    return  
}  
  
// newNum generate a random number
func newNum() int {
    // To generate different sequences, we need to give a varying seed value 
    s := rand.NewSource(time.Now().UnixNano())  
    r := rand.New(s)  
    return r.Intn(100)  
}  
  
// inputNum Input user number  
func inputNum(stdin *bufio.Reader) (int, error) {  
    var n int  
    _, err := fmt.Scanln(&n)  
    if err != nil {  
       _, _ = stdin.ReadString('\n') // empty the input stream  
       return 0, errors.New("scan error")  
    }  
    return n, nil  
}

# References