3. Data Types

3.1 Is Go statically or dynamically typed?

Go is a strongly and statically typed language, which means that a variable of one type is known at compile time and can not be changed or automatically converted to another type throughout the program execution [1].

Although Go is a strongly typed language, it supports both explicitly and implicitly typed. Go infers the type of untyped variables from its value. The default type of an untyped constant is bool, int, float64, complex128, or string [2].

// implicit-explicit-typed.go
package main
import (
  "fmt"
  "reflect"
)
  
func main(){
  var a int = 5 
  b := 3.5
  fmt.Println("type of a is", reflect.TypeOf(a))
  fmt.Println("type of b is", reflect.TypeOf(b))
}

You will receive the following output for the above code

type of a is int
type of b is float64

3.2 Basic Data Types

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // alias for uint8

rune // alias for int32
     // represents a Unicode code point

float32 float64

complex64 complex128

When a variable is declared without an explicit initial value, it is given the value that corresponds to 0.

  • Numerics: 0

  • Boolean: false

  • String: ‘’ (the empty string)

Note: The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems.

// basic-data-types.go
package main

import (
	"fmt"
	"math/cmplx"
)

func main() {
	var studentName string
	var studentAge int
	var studentGPA float32
	var isFemale bool
	var complexNum complex128

	studentName = "Student A"
	studentAge = 19
	studentGPA = 3.87
	isFemale = true
	complexNum = cmplx.Sqrt(-5 + 12i)

	fmt.Println(studentName, studentAge, studentGPA, isFemale, complexNum)

  var nonInt int
  var nonBool bool
  var nonString string
  fmt.Println(nonInt, nonBool, nonString)
}

Results:

>> Student A 19 3.87 true (2+3i)
>> 0 false ''

Note that you can not do operations on variables of different data types (even between int and float). If you run the following code, it will raise an error says invalid operation: studentGPA + studentAge (mismatched types float32 and int).

package main

import (
	"fmt"
	"math/cmplx"
)

func main() {
	
	var studentAge int
	var studentGPA float32
	fmt.Println(studentGPA + studentAge)
}

For division, if you take 5 and you divide it into 2, you will get a 2 instead of 2.5. However, like any other languages, Go allows you to convert from one basic data type to another, just simply wrap the type name outside the variable. In the following example, b is a float conversion of int a. We use reflect module to print out the type of a and b.

// type-conversion.go
package main

import (
	"fmt"
	 "reflect"
)

func main() {
	var a = 5
	var b float64 = float64(a/2)
	fmt.Println("a =", reflect.TypeOf(a))
	fmt.Println("b =", reflect.TypeOf(b))
}

You should expect the following output when you run the above code.

a = int
b = float64

3.3 Operators

3.3.1 Arithmetic Operators

Arithmetic operators apply to numeric values and yield a result of the same type as the first operand. The following list is a summary of all arithmetic operators in Go [1].

+    sum                    integers, floats, complex values, strings
-    difference             integers, floats, complex values
*    product                integers, floats, complex values
/    quotient               integers, floats, complex values
%    remainder              integers

&    bitwise AND            integers
|    bitwise OR             integers
^    bitwise XOR            integers
&^   bit clear (AND NOT)    integers

<<   left shift             integer << integer >= 0
>>   right shift            integer >> integer >= 0

3.3.2 String Concatenation

Strings can be concatenated using the + operator or the += assignment operator. String addition creates a new string by concatenating the operands [1].

// string-concatenation.go
package main
import (
  "fmt"
)
  
func main(){
    var s1 = "hello"
    var s2 = " world"
    var s3 = s1 + s2
    fmt.Println(s3)
}

The above code will print out

hello world

3.4 Composite Data Types

Besides the basic data types, Go supports other 8 composite data types, namely array, struct, pointer, function, interface, slice, map, and channel types [2].

3.4.1 Array

Array is a list of elements of the same type. It can not have mixed-typed elements. The length of an array is its number of elements. The length of an array can be evaluated by the function len()

// array.go
package main

import "fmt"

func main() {
	var arr [10]string

	arr[3] = "hello"
	fmt.Println("arr is", arr)
	fmt.Println("arr[0] is", arr[0])
	fmt.Println("arr[3] is", arr[3])
	fmt.Println("len(arr) is", len(arr))

  // this statement will cause error because arr is an array of string
	// arr[0] = 1
}

You should receive the following output

arr is ['', '', '', 'hello', '', '', '', '', '', '']
arr[0] is ''
arr[3] is 'hello'
len(arr) is 10

3.4.2 Struct

Struct is a sequence of elements of the same or different types.

// struct.go
package main

import (
	"fmt"
)

type student struct {
	name string
	GPA  float64
}

func main() {
	studentA := student{name: "A", GPA: 3.8}
	fmt.Println(studentA)
	fmt.Println(studentA.name)
	fmt.Println(studentA.GPA)
}

You should receive the following output:

{A 3.8}
A
3.8

3.4.3 Pointer

Pointer stores the memory address of the variable.

Type *T is a pointer that points to a value of type T. Its value without initialization is nil.

The & operator creates a pointer to the variable.

The * operator evaluates the value pointed by the pointer.

// pointer.go
package main

import (
	"fmt"
)

func main() {
	var p *int // initialize pointer variable
	i := 4
	p = &i
	fmt.Println("==Before==")
	fmt.Println("i:", i) // print value of i on its initialization
	fmt.Println("p:", p) // print pointer p (the memory address of i)
	*p = *p / 2          // take the value stored in p and divide it by 2
	fmt.Println("==After==")
	fmt.Println("i:", i) // print p again to see if the memory address changes
	fmt.Println("p:", p) // print value of i to see if its value changes
}

You should receive the following output:

==Before==
i: 4
p: 0xc000096008
==After==
i: 2
p: 0xc000096008

3.4.4 Function

Function type defines functions in Go

// function.go
package main

import "fmt"

func add(a int, b int) int {
	return a + b
}

func main() {
	fmt.Println(add(1, 2))
}

When you run this code, you should receive an output 3.

3.4.5 Interface

See chapter 8. Inheritance

3.4.6 Slice

Slice is a data type that is associated with an array. It provides a way to access a block of consecutive elements in an array. It is initialized by a slice constructor, and each slice object associates with only one array. Unlike array, slice can extends its size as long as its size belongs to the parent array.

package main

import "fmt"

func main() {
	odds := [10]int{1, 3, 5, 7, 9}

	var odd []int = odds[0:3]

	fmt.Println(odd)
}

Output

[1,3,5]

3.4.7 Map

Map data type in Go is similar to dictionary in python, where each key is attached with a value.

// map.go
package main

import "fmt"

func main() {
	m := make(map[string]int)
	m["ten"] = 10
	m["one"] = 1
	fmt.Println(m["ten"])
}

In this example, we map the word form of a number with its numeric value. You should receive 10 in the output.

3.4.8 Channel

Channel is a unique data type in Go. As Go is designed for concurrency, it creates Channel as a way to send and receive values of a specific data types across execution threads. Here is a toy example taken from Go by Example: Channels

// channel.go
package main
import "fmt"

func main() {
    messages := make(chan string)
    go func() { messages <- "ping" }()
    msg := <-messages
    fmt.Println(msg)
}

The above code will output

ping

3.5 Mutability

Go has both mutable and immutable objects [3]

Mutable Go objects:

  • arrays

  • slices

  • maps

  • channels

  • structs having multiple fields

Immutable Go objects:

  • interfaces

  • booleans

  • int

  • float

  • strings

  • pointers

  • structs having a single field