Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
444 lines (328 sloc) 13.8 KB

Basic composite types

In the :doc:`previous chapter<functions>`, we saw how to combine the traditional control flow idioms seen in :doc:`chapter 3<control>` to create blocks of reusable code, called functions. In this chapter, we'll see how to combine the basic data types seen in :ref:`chapter 2<primitive-types>`, in order to create complex data types.

The problem

Go comes with some primitive types: ints, floats, complex numbers, booleans, and strings. Fair enough, but you'll notice as you start to write complex programs that you'll need more advanced forms of data.

Let's begin with an example.

In the previous chapter, we wrote the MAX(A, B int) function to find out the bigger value between two integers A, and B. Now, suppose that A, and B are actually the ages of persons whose names are respectively Tom and Bob. We need our program to output the name of the older person, and by how many years he is older.

Sure, we still can use the MAX(A, B int) function by giving it as input parameters, the ages of Tom and Bob. But it'd be nicer to write a function Older for people. And not for integers.

People! I said people! Thus the need to create a custom type to represent people, and this type will be used for the input parameters of the Older function we'd like to write.


In Go, as in C and other languages, we can declare new types that act as containers for attributes or fields of some other types. For example, we can create a custom type called person to represent the attributes of a person. In our example these attributes will be the person's name and age.

.. graphviz::

    digraph person {
        graph [bgcolor=transparent, resolution=96, fontsize="10" ];
        edge [arrowsize=.5, arrowtail="dot", color="#FF6600"];
        node [shape=record, fontsize=8, height=.1, penwidth=.4]
        pName [label=""];
        pAge [label="P.age"];

A type like this is called a struct, which is short for structure.

How to declare a struct in Go?

See? It's easy. A person struct is a container of two fields:

  • The person's name: a field of type string.
  • The person's age: a field of type int.

To declare a person variable we do exactly as we learned in :doc:`chapter 2<basic>` for basic variable types.

Thus, writing things like:

We access and assign a struct's fields (attributes) with the dot nottation. That is, if P is a variable of type person, then we access its fields like this:

There's even two short-form assignment statement syntaxes:

  1. By providing the values for each (and every) field in order:
  1. By using field:value initializers for any number of fields in any order:

Good. Let's write our Older function that takes two input parameters of type person, and returns the older one and the difference in their ages.


Of Tom and Bob, Bob is older by 7 years
Of Tom and Paul, Paul is older by 25 years
Of Bob and Paul, Paul is older by 18 years

And that's about it! struct types are handy, easy to declare and to use!

Ready for another problem?

Now, suppose that we'd like to retrieve the older of, not 2, but 10 persons! Would you write a Older10 function that would take 10 input persons? Sure you can! But, seriously, that would be one ugly, very ugly function! Don't write it, just picture it in your brain. Still not convinced? Ok, imagine a Older100 function with 100 input parameters! Convinced, now?

Ok, let's see how to solve these kinds of problems using arrays.


An array of size n is a block of n consecutive objects of the same type. That is a finite set of indexed objects, where you can enumerate them: object number 1, object number 2, etc...

In fact, in Go as in C and other languages, the indexing starts from 0, so you actually say: object number 0, object number 1... object number n-1

.. graphviz::

    digraph array_s {
        graph [bgcolor=transparent, resolution=96, fontsize="10" ];
        edge [arrowsize=.5, arrowtail="dot", color="#555555"];
        node [shape=record, fontsize=8, height=.1, penwidth=.4]

Here's how to declare an array of 10 ints, for example:

And here's how to declare an array of 10 persons:

We can access the x th element of an array A, with the syntax: A[x]. Specifically we say: A[0], A[1], ..., A[n].

For example, the name of the 3 rd person is:

.. graphviz::

    digraph array_s {
        graph [bgcolor=transparent, resolution=96, fontsize="10" ];
        edge [arrowsize=.5, arrowhead="dot", color="#555555"];
        node [shape=record, fontsize=8, height=.1, penwidth=.4]
        p2[shape=circle, penwidth=1, color=crimson, fontcolor=black, label=<
                <TD BGCOLOR="lightyellow">Tom</TD>
                <TD BGCOLOR="lightblue">25</TD>
        p2->people:f2 [label=" Zoom on people[2]", fontsize=6, color=crimson, fontcolor=crimson]

The idea now is to write a function Older10 that takes an array of 10 persons as its input, and returns the older person in this array.


The older of the group is: Sam

We could have declared and initialized the variable array of the main() function above in a single shot like this:

This means that to initialize an array, you put its elements between two braces, and you separate them with commas.

We can even omit the size of the array, and Go will count the number of elements given in the initialization for us. So we could have written the above code as:

Specifically note the ellipsis [...].


However, the size of an array is an important part of its definition. They don't grow and they don't shrink. You can't, for example, have an array of 9 elements, and use it with a function that expects an array of 10 elements as an input. Simply because they are of different types. Remember this.

Some things to keep in mind:

  • Arrays are values. Assigning one array to another, copies all of the elements of the right hand array to the left hand array. That is: if A, and B are arrays of the same type, and we write: B = A, then B[0] == A[0], B[1] == A[1]... B[n-1] == A[n-1].
  • When passing an array to a function, it is copied. i.e. The function recieves a copy of that array, and not a reference (pointer) to it.
  • Go comes with a built-in function len that returns variables sizes. In the previous example: len(array) == 10.

Multi-dimensional arrays

You can think to yourself: "Hey! I want an array of arrays!". Yes, that's possible, and often used in practice.

We can declare a 2-dimensional array like this:

This is an array of (2 arrays (of 4 int)). We can think of it as a matrix, or a table of two lines each made of 4 columns.

.. graphviz::

    digraph array_2 {
        graph [bgcolor=transparent, resolution=96, fontsize="10" ];
        edge [arrowsize=.5, arrowtail="dot", color="#ff6600"];
        node [shape=record, fontsize=8, height=.1, penwidth=.4]
        node[shape="plaintext", fixedsize="true"]
        exnode00 [label="A[0,0]"];
        exnode01 [label="A[0,1]"];
        exnode11 [label="A[1,1]"];
        exnode13 [label="A[1,3]"];

The previous declaration may be simplified, using the fact that the compiler can count arrays' elements for us, like this:

Guess what? Since Go is about cleaner code, we can simplify this code even further:

Cool, eh? Now, we can combine multiple fields of different types to create a struct and we can have arrays of, as many as we want, of objects of the same type, and pass it as a single block to our functions. But this is just the begining! In the next chapter, we will see how to create and use some advanced composite data types with pointers.