Go Basics

Go is a procedural, imperative, modular, object-oriented, statically-typed, garbage-collected, compiled programming language with syntax broadly similar to the C family of languages (C, C++, C#, Java, Javascript, etc...) and CSP-inspired concurrency features. We will examine each of those descriptions in greater detail, but before we do lets write our first Go program.

Compilation

Go is a compiled programming language. The Go compiler takes source code and translates it into the native language the computer speaks (machine code). Go programs are broken up into packages, with a package called main being the entry point for a program. In turn each package can be broken up into multiples files.

Although not strictly required, the conventional approach to writing programs in Go, and the one enshrined by the go commands, is to use a unified source code tree of files and folders. The Go compiler looks for code in one of two locations:

At a minimum both locations contain 3 folders:

Packages are found by taking their name and appending it to GOPATH/src or GOROOT/src. For example there is a package in the standard library called time and we can find the source code for it by doing the following:

  1. Open a terminal and enter the following command:

    go env

    This will print a variety of environment variables, one of which is GOROOT. On my computer this is /usr/local/go.

  2. Take your GOROOT folder and list its contents:

    ls /usr/local/go

    On my computer this displays:

    api  AUTHORS  bin  blog  CONTRIBUTORS  doc  favicon.ico  include  lib  LICENSE  misc  PATENTS  pkg  README  robots.txt  src  test  VERSION

    Notice that we see the same 3 folders discussed above: bin, pkg and src.

  3. We should expect to find the time package's source code in the GOROOT/src/time folder. Let's confirm that:

    ls /usr/local/go/src/time

    You should see something like this:

    example_test.go         sleep_test.go   zoneinfo_abbrs_windows.go
    export_test.go          sys_plan9.go    zoneinfo.go
    export_windows_test.go  sys_unix.go     zoneinfo_plan9.go
    format.go               sys_windows.go  zoneinfo_read.go
    format_test.go          tick.go         zoneinfo_test.go
    genzabbrs.go            tick_test.go    zoneinfo_unix.go
    internal_test.go        time.go         zoneinfo_windows.go
    sleep.go                time_test.go    zoneinfo_windows_test.go

    Those definitely look like time-related files.

So the time package's files can be found (on my computer) in the $GOROOT/src/time folder. For the code write we need to put files in $GOPATH/src.

Hello World

Based on this folder structure, we can now create our first Go program.

  1. Create a folder for our program:

    mkdir -p $GOPATH/src/example/hello
  2. Open your text editor and create a new file called main.go and save it in that folder with the following contents:

    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("Hello World")
    }
    
  3. Compile this program by running:

    go install example/hello
  4. Run the program:

    $GOPATH/bin/hello

    Or if $GOPATH/bin has been added to PATH, just:

    hello

The go install compiles and links our Hello World program into an executable binary in the bin folder. We could've also used the go build command which we can call like this:

go build -o /tmp/hello example/hello

If we had done so this would've saved the binary as /tmp/hello instead of $GOPATH/bin/hello.

Packages

Go source code files always start with a package clause. The grammar states:

SourceFile    = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
PackageClause = "package" PackageName .
PackageName   = identifier .

There are two types of packages: main packages which have package main as their package clause, or all other packages which should have a package name which matches their folder. For example the files that make up the fmt package contain package fmt:

cat /usr/local/go/src/fmt/scan.go
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fmt

import (
    "errors"
    "io"
    "math"
    "os"
    "reflect"
    "strconv"
    "sync"
    "unicode/utf8"
)

...

Packages like this can't be executed directly, but rather they are meant to be imported by other packages. Indeed in our Hello World program, we see that immediately after the package clause we import the fmt package:

import "fmt"

The Go compiler sees this, compiles all the code in fmt to a temporary file in $GOROOT/pkg/OS_ARCH/fmt.a, and then links that with everything defined in the main package.

The important thing to remember is that the Go compiler links packages and not files. In most interpreted languages, for example PHP, the primary focus is on an entry file, which then includes other files. It's as if you took the source code in those other files and then replaced the include statement with that code. The same thing happens in C or C++ with preprocessor directives.

Go is different in that a set of files is first compiled into a set of exported elements, which are then used by other packages. This is why we import a package, and not a specific file inside of the package.

As we can see above with the fmt source code, imports are recursive. We import fmt in main, which then imports errors, and so on. We can visualize this as a tree:

To recap, Go is a modular programming language where programs are made up of packages, which are made up of files. Every Go source code file has a package clause, and to use packages besides main we use the import declaration.

Functions

The next element in our Hello World program is a function definition.

func main() {
    fmt.Println("Hello World")
}

Functions are the building blocks of a Go program. They contain a sequence of instructions called statements which we want our computer to perform. In other programming languages functions are also called routines, sub-routines, procedures or methods.

In Go a function declaration is described by the grammar like this:

FunctionDecl  = "func" FunctionName ( Function | Signature ) .
FunctionName  = identifier .
Function      = Signature FunctionBody .
FunctionBody  = Block .
Block         = "{" StatementList "}" .

Functions are defined with the func keyword, followed by a function name, a function signature and a function body. In our program the function name is main, the function signature is () (meaning it takes no arguments and returns nothing) and the function body is a block, which is a sequence of statements surrounded by curly braces:

{
    fmt.Println("Hello World")
}

Our block has only one statement. For additional statements we either need to separate them with a semicolon:

{
    fmt.Println("Statement #1") ; fmt.Println("Statement #2")
}

Or a newline:

{
    fmt.Println("Statement #1")
    fmt.Println("Statement #2")
}

The latter is the preferred approach. (With the former being so unusual that many Go programmers don't even realize it can be done) We will look at functions in more detail later.

The function named main is special because it is the entry point for our program. Every Go program must have one (and only one) main function inside of the main package.

Statements and Expressions

Every function contains zero or more statements. In our main function we only have one:

fmt.Println("Hello World")

This is known as an expression statement. An expression statement is a statement made up of a single expression. The specification states:

An expression specifies the computation of a value by applying operators and functions to operands.

Expressions are like their mathematical counterpart. Consider a basic arithmetic expression:

a + b

In this expression a and b are operands and + is the operator. We can also use a function instead of an operator:

sin a

sin is a function, though Go functions aren't quite the same. For one they always require parentheses around the arguments:

sin(a)

And for another, when referencing a function from another package we use a qualified identifier:

math.Sin(a)

math.Sin refers to the Sin function in the math package. Since math is a package, we would also need a corresponding import declaration at the top of the file:

package main

import "math"

func main() {
    math.Sin(1.234)
}

If you're wondering why the function is capitalized it's because only capitalized functions are exported from packages.

If we go back to our Hello World statement:

fmt.Println("Hello World")

We now know that we are calling a function in the fmt package named Println and passing it the string Hello World. When executed this expression results in Hello World being output to the terminal.

So Go programs are built out of packages, which are made up of files, which include functions each of which has a series of statements and statements are made up of expressions, which are, in turn, made up of operands, operators and function calls.

In a sense you can think of a Go program like a book, where each package is a chapter of that book, each function is a paragraph, each statement is a sentence and each expression is word or phrase.

Our computer reads this book and does everything it says to do. Most statements are commands ("add these numbers", "print this to the screen", etc..) and most paragraphs include references to other paragraphs - even paragraphs from other chapters.

Go is a procedural programming language because procedures (functions) are stitched together to form a program. It is an imperative programming language because functions are built out of statements which are generally phrased as imperative commands.

Problems

  1. Create a program which outputs Hello NAME where NAME is replaced with your name. For example:

← Previous Index Next →