Uncategorized

Go Data Types

Basic data types

bool 
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // same as for uint8
rune // represents a Unicode code point
float32 float64
complex64 complex128

int

int is a signed integer type that is at least 32 bits in size. It is a distinct type, however, and not an alias for, say, int32

An int is either 32 or 64 bits depending on the platform. So it maybe better to be specific if developing and host machines differ.

Rune

Strings are made up of “runes” rather than chars. Rather than having char and wchar for characters and unicode characters, a rune is a “code point” as defined in the Unicode standard. Internally it is  a 32-bit representation.

nihao := "你好" 
for index, runeValue := range nihao {
fmt.Printf("Char %#U Byte position %d\n", runeValue, index)
}
  • String to array of runes
runes := []rune(s)
  • rune array to string
str := string(runes)

Simple Composite Types

Slice

A slice consists of a pointer to an array segment, length of the segment and its capacity i.e. the maximum length of the segment.

  • Create
b = make([]T, len(a))

Note that since the zero value of a slice is nil, you don’t need to explicitly make a slice, you can just declare it like below.

  • Add element
var s []int      // s := make([]string, 0) 
s = append(s, 1) //add one element
s = append(s, 2,3,4) //add multiple
s = append(s, s1…) // append another slice
  • Delete element
s = s[:len(s)-1] //remove last element

Be aware that if a slice contains pointers to elements and a delete is done, there maybe a potential memory leak. Values referenced by the removed slice elements may not be garbage collected as the references are not totally removed.

  • Remove all elements of a slice

To remove all elements of a slice, set it to nil. This will release it to the garbage collector as long as there no other references to it.

s = nil
  • Iterate over a slice
for index, value := range s { 
fmt.Println(index, value)
}
  • Copy

Use copy to deep copy all slice elements from one slice to another. Simply assigning one slice will only change the reference.

a := []int{1, 2}
b := []int{3, 4}

a = b // changes a to refer to the location of b

copy(a,b) //actually copy values from b to a

https://play.golang.org/p/X_WC1eN7IHCh

The number of elements copied is the minimum of len(src) and len(dst). For example in the below code the destination slice will have no elements since len(dst) is 0

s := []int{4, 2,3, 1}
var dst []int
copy(dst, s)
fmt.Println(dst)
fmt.Println(s)

Output:
[]
[4 2 3 1]

We need to allocate a length when creating the destination slice

dst := make([]int, len(s)
copy(dst, s)

https://play.golang.org/p/Sp4NoX4YaaA

Two dimensional slices

Slices in Go are one-dimensional. We have to compose it to form two-dimensional arrays.

matrix := make([][]int, row) 
for m := range matrix {
matrix[m] = make([]int, col)
}

When you pass a slice to a function, since it is a pointer to an array you just get a copy of the slice structure. It will still point to the same underlying array segment. So any modifications made to the slice within the function will be seen outside.

https://play.golang.org/p/OLkoxGf8uaZ

However, if you append an element, remember that a new slice is created and elements are copied over so you will lose the elements if this happens within a function so you must return a slice. E.g. append from the stdlib returns a new slice.

  • Creating and initializing a 2d slice
graph := [][]int{  
{0, 4, 0, 0, 0},
{4, 0, 8, 0, 0},
{0, 8, 0, 7, 0},
{0, 0, 7, 0, 9},
{0, 0, 0, 9, 0},
}

Map

The built-in map type implements a hash table.


Maps, like channels, but unlike slices, are just pointers to runtime types.

Map is a pointer to a runtime.hmap structure. So when we declare a map using var, its value will be nil until initialized using make. Attempts to access a nil map will result in a panic.

Maps are not concurrent safe. So proceed with caution when reading/writing to them in more than one go routine.

  • Create
x := make(map[int]string)

You could initialize a map with values. In that case, you don’t have to use make

colorCodes := map[string]string{  
"BRONZE":   "E0001B",
"GOLD":    "A89968",
"SILVER":   "888B8D",
}
fmt.Println(colorCodes["BRONZE"])
  • Add element to a map
x[1] = 10
  • Delete element
delete(x, 1)
  • Find value
val, ok := x[key]
  • Iterating
for key, val:= range x {     
println("Key:", key, "Value:", val ,"Value:", m[k])
}
  • Size
len(m)
  • Copy

To copy a map to another, we need to traverse the map and do a deep copy. Just assigning a map to another will not copy contents over.

https://play.golang.org/p/JiuMeE3rClf

All operations on a map are in constant time. Most map operations like lookup, insertion etc. are rewritten at compile time to runtime calls. 

You don’t need to use references with a map. Map types are reference types like slices.  If you pass a map to a function and add key-value pairs or update values , it will be reflected outside a function. When using maps in concurrent routines, we need to add locks to ensure data integrity.

https://play.golang.org/p/j7dD1K_rfRY

Struct Type

struct is a collection of fields.

A struct can have methods defined on it. There are two kinds of methods that can be defined on a  struct, a value receiver and a pointer receiver.

type T struct { 
a int
}

func (tv T) Mv(a int) int {
return 0
} // value receiver

func (tp *T) Mp(f float32) float32 {
return 1
} // pointer receiver

It all comes down which one is better for the problem at hand.

Related blog posts for Structs:

Constants

There are boolean constantsrune constantsinteger constantsfloating-point constantscomplex constants, and string constants. Rune, integer, floating-point, and complex constants are collectively called numeric constants.

A constant expression may contain only constant operands and are evaluated at compile time. As such, there is no such thing as array constant in Go. Slices and arrays are always evaluated during runtime. They can only be defined as variables.

Auto increment constants can be defined using iota.

const (
Black = iota
White
Red
Yellow
Green
)

fmt.Println(Black, White, Red, Yellow, Green)

Output:
0 1 2 3 4

Equality of data types

Simple data types and individual elements can be compared using the equality operator ==

Arrays and slices can only be compared to nil.

For other complex datatypes we can use the DeepEqual function in the reflect package.

package main
import (
"fmt"
"reflect"
)

func main(){
a := []int{3,4,5}
b := []int{3,4}
b = append(b, 5)
if reflect.DeepEqual(a,b){
fmt.Println("slices a and b are equal")
}
}

Struct values can also be compared using DeepEqual. The exported as well as unexported values are compared for equality.

Channels can be compared like numbers and strings using the == operator.

Related:

Interface Types