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.
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:
GOROOT
which is usually defined implicitely based on your
Go installation (e.g. /usr/local/go), and contains all of the source code
for packages that come with the standard
library
GOPATH
which must be defined by an environment variable and
contains the code you write or 3rd party packages
At a minimum both locations contain 3 folders:
bin
which contains the compiled, executable binaries
produced by the Go compiler
pkg
which contains partial, intermediate files use to
improve compiler performance
src
which contains the actual Go source code
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:
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
.
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
.
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
.
Based on this folder structure, we can now create our first Go program.
Create a folder for our program:
mkdir -p $GOPATH/src/example/hello
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") }
Compile this program by running:
go install example/hello
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
.
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.
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.
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.
Create a program which outputs Hello NAME
where
NAME
is replaced with your name. For example:
← Previous | Index | Next → |