Composite types

Go supports 8 different composite types as specified by the language specification [1], namely array, struct, pointer, function, interface, slice, map, and channel types. Official definitions of all the types are quoted from the langauge specification.

Array

An array is a numbered sequence of elements of a single type, called the element type. The number of elements is called the length and is never negative.

All the indexes should be non-negative integers and all the entries should be the same type. Array is a mapping in Go with integer as indexes. An example is shown below.

// go run go-array.go
package main
import "fmt"

func main(){
    var a [10]int
    fmt.Println("array: ", a)

    fmt.Println("a[0] is ", a[0])
    a[0] = 10; // set the a[0] to 10
    fmt.Println("a[0] is ", a[0])
}

You should see the the following output:

array:  [0 0 0 0 0 0 0 0 0 0]
a[0] is  0
a[0] is  10

Struct

A struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or implicitly (AnonymousField). Within a struct, non-blank field names must be unique.

The struct is the cartesian product type in Go. Each elements should have a name and a type. Similar to C, struct doesn't have a methods inside the declaration. In other words, methods are defined on structs, not within structs.

An example is shown below:

// go run go-struct.go
package main
import "fmt"

type banana struct{
    foo int
    bar string
}

func main(){
    ba := banana{foo : 1, bar: "hello"}
    fmt.Println("ba's foo is", ba.foo)
    fmt.Println("ba's bar is", ba.bar)
}

You should see the following output:

ba's foo is 1
ba's bar is hello

Pointer

A pointer type denotes the set of all pointers to variables of a given type, called the base type of the pointer. The value of an uninitialized pointer is nil.

Pointers in Go allow us pass references in the program. An example is shown below.

// go run go-pointer.go
package main
import "fmt"

func print_int_pointer(p *int){
    fmt.Println("The value of the pointer is", *p)
}

func main(){
    i := 42;
    print_int_pointer(&i);
}

you should see the following output.

The value of the pointer is 42

Function

A function type denotes the set of all functions with the same parameter and result types. The value of an uninitialized variable of function type is nil.

Functions are mappings in Go. They're also first-class objects. An interesting note about the function is that if you want to modify the member of a struct, you have to pass the pointer of the struct, which will be illustrated in the interface section. An example of a simple calculator is shown below:

// go run go-function.go
package main
import "fmt"
type newFunc func(int , int) int
func main(){
    var a = 1
    var b = 2
    fs := []newFunc{
        func(a int, b int) int {return a+b},
        func(a int, b int) int {return a-b},
        func(a int, b int) int {return a*b},
        func(a int, b int) int {return a / b},
    }
    var fn = fs[0]
    fmt.Println("a + b = ", fn(a, b))
    fn = fs[1]
    fmt.Println("a - b = ", fn(a, b))
    fn = fs[2]
    fmt.Println("a * b = ", fn(a, b))
    fn = fs[3]
    fmt.Println("a / b = ", fn(a, b))
}

You should see the following result:

a + b =  3
a - b =  -1
a * b =  2
a / b =  0

Interface

An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.

An example of interface is shown below

// go run go-interface.go
package main
import "fmt"

type pet interface{
    run()
    eat()
}

type cat struct{
    weight int
}

func (c *cat) run(){
    c.weight--;
}

func (c *cat) eat(){
    c.weight += 4;
}

func take_care(p pet){
    p.run()
    p.eat()
}

func main(){
    c := cat{weight: 10}
    fmt.Println("Before taking care, cat is", c.weight)
    take_care(&c)
    fmt.Println("After taking care, cat is", c.weight)
}

You should see the following output:

Before taking care, cat is 10
After taking care, cat is 13

Slice

A slice is a descriptor for a contiguous segment of an underlying array and provides access to a numbered sequence of elements from that array. A slice type denotes the set of all slices of arrays of its element type. The value of an uninitialized slice is nil.

Slice is a very interesting thing in Go. It behaves very similarly to the list in Python, although it cannot grow by itself automatically. A slice can be created by a slice constructor, or by slicing a array. Here is an example using slices in go.

// go run go-slice.go
package main
import "fmt"

func main(){
    slice1 := make([]int, 3)
    slice1[0] = 2;
    slice2 := slice1[0:2]
    fmt.Println("slice1[0]", slice1[0])
    fmt.Println("slice2[0]", slice2[0])
    slice1[0] = 3
    fmt.Println("After modification\nslice1[0]", slice1[0])
    fmt.Println("slice2[0]", slice2[0])
}

You should see the following result:

slice1[0] 2
slice2[0] 2
After modification
slice1[0] 3
slice2[0] 3

One interesting thing in the example to notice is that the slice only holds the reference. It usually doesn't have their own memory storage, unlike the array type.

Map

A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type. The value of an uninitialized map is nil.

Maps are the dictionary in other languages. The usage is very similar to other languages as well. An example is shown in the code below:

// go run go-map.go
package main
import "fmt"

func main() {
   m := make(map[int]string)
   m[1] = "hello"
   m[42] = "world"
   fmt.Println(m[1], m[42])
}

You should see the following output:

hello world

Channels

A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type. The value of an uninitialized channel is nil.

Channels are built-in pipes for concurrent entities to communicate with each other. Since Go is a concurrent language, it has many nice features in thier language to support concurrency. Here is a simple example to use Channels taken from Go By Example [2].

// go run go-channels.go
package main
import "fmt"

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

You should see ping as output result.

[1] https://golang.org/ref/spec

[2] https://gobyexample.com/channels