10 KiB
sitename | title | template |
---|---|---|
Within | Go Training 1: Get Going | blog |
This post is the first in a series I'm creating to help people learn the Go programming language. It's aimed at people who understand the high level concepts of programming, but haven't had much practical experience with it.
Like always, feedback is very welcome. Any feedback I get will be used to help make this course even better.
What is Go?
Go is a compiled programming language made by Google. It has a lot of features out of the box, including:
- A static type system
- Fast compile times
- Efficient code generation
- Parallel programming for free*
- A strong standard library
- Cross-compilation with ease (including webassembly)
- and more!
* You still have to write code that can avoid race conditions, more on those later.
Why Use Go?
Go is a very easy to read and write programming language. Consider this snippet:
func Add(x int, y int) int {
return x + y
}
This function wraps integer addition. When you call it it returns the sum of x and y.
Installing Go
Linux
Installing Go on Linux systems is a very distribution-specific thing. Please see this tutorial on DigitalOcean for more information.
macOS
- Go to https://golang.org/dl
- Download the .pkg file
- Double-click on it and go through the installer process
Windows
- Go to https://golang.org/dl
- Download the .msi file
- Double-click on it and go through the installer process
Next Steps
These next steps are needed to set up your shell for Go programs.
Pick a directory you want to store Go programs and downloaded source code in.
This is called your GOPATH. This is usually the go
folder in
your home directory. If for some reason you want another folder for this, use
that folder instead of $HOME/go
below.
Linux/macOS
This next step is unfortunately shell-specific. To find out what shell you are using, run the following command in your terminal:
$ env | grep SHELL
The name at the path will be the shell you are using.
bash
If you are using bash, add the following lines to your .bashrc (Linux) or .bash_profile (macOS):
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
Then reload the configuration by closing and re-opening your terminal.
fish
If you are using fish, create a file in ~/.config/fish/conf.d/go.fish with the following lines:
set -gx GOPATH $HOME/go
set -gx PATH $PATH $GOPATH/bin
zsh
If you are using zsh, add the following lines to your .zshrc:
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
Windows
Follow the instructions here.
Hello, world!
Now that everything is installed, let's test it with the classic "Hello, world!"
program. Create a folder in your code folder called go_learning
and create a
subfolder called hello
. Open a file in there called hello.go
in your
favorite text editor and type in the following:
// Command hello is your first Go program.
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}
This program prints "Hello, world!" and then immediately exits. Here's each of the parts in detail:
// Command hello is your first go program.
package main // Every go file must be in a package.
// Package main is used for creating executable files.
import "fmt" // Go doesn't implicitly import anything. You need to
// explicitly import "fmt" for printing text to
// standard output.
func main() { // func main is the entrypoint of the program, or
// where the computer starts executing your code
fmt.Println("Hello, world!") // This prints "Hello, world!" followed by a newline
// to standard output.
} // This ends the main function
Now to run this program run the following command:
$ go run hello.go
Hello, world!
go run
compiles and runs the code for you, without creating a persistent binary
file. This is a good way to run programs while you are writing them.
To create a binary, use go build
:
$ go build hello.go
$ ./hello
Hello, world!
go build
has the compiler create a persistent binary file and puts it in the
same directory as you are running go
from. Go will choose the filename of the
binary based on the name of the .go file passed to it. These binaries are
usually static binaries, or binaries that are safe to distribute to other
computers without having to worry about linked libraries.
Go Modules
Go modules are the current state of the art of dependency and project management for programs written in Go. We will cover them in much more detail in the future, but for now create a module for your hello command with the following command:
$ go mod init your-name.localhost/hello
go: creating new go.mod: module christine-dodrill.localhost/hello
This creates a file called go.mod that tells the compiler details about your project. Again, we will cover this in more detail in the future, but just know this exists for now.
Creating Other Files
Go packages map to folders on the filesystem. This means that every .go file in the same filesystem folder must be inside the same package in order for the package to compile. There are some edge case exceptions to this rule, of course, but those will be covered later.
Adding Numbers
Create a new file called math.go
with the following contents:
package main
// Add adds two numbers and returns the sum.
func Add(x, y int) int {
return x + y
}
This file creates a function called Add
that adds the numbers it is given.
This function is also documented because there's a comment immediately above it
explaining what it does. We can view the documentation with the go doc
command:
$ go doc -all
Command hello is your first Go program.
FUNCTIONS
func Add(x, y int) int
Add adds two numbers and returns the sum. This helps you understand functions.
Go automatically keeps track of documentation comments and lets you query them
with go doc
. These comments should usually help explain why things are the
way they are. That helps your future self understand what is going on there.
Dividing Numbers
Go allows functions to return multiple values. When a function returns a result but can also results in an error it is a common idiom to return two values: the result and the error.
Unlike Java and Python, Go does not have have Exceptions that get thrown. Instead, errors are regular values returned by functions.
We can model this idiom with integer division, where anything divided by zero is famously undefined.
Add the following to math.go
:
// ErrDivideByZero is returned when Divide would divide by zero.
var ErrDivideByZero = errors.New("divide by zero")
// Divide performs floating-point division and returns the result. If the right
// hand side of Divide is zero, it returns ErrDivideByZero to avoid a panic.
func Divide(lhs, rhs float64) (float64, error) {
if rhs == 0 {
return 0, ErrDivideByZero
}
return lhs / rhs, nil
}
And add this to the top of math.go
, just below the package
line:
import "errors"
We need to import the errors package to create the ErrDivideByZero error. This error will be returned when the left-hand-side of the Divide function is zero.
Weaving it Together
Now these functions in math.go
are usable from hello.go
. Let's add a few
numbers after the "Hello, world!" call.
var sum int = Add(2, 2)
fmt.Println("2 + 2:", sum)
This creates an integer variable called sum and sets it to the result of adding 2 and 2 together. You can also create the sum variable by doing this:
sum := Add(2, 2)
The :=
operator automagically declares the variable on its left hand side with
the value on its right hand side.
Now run it with go run .
. The .
argument tells go run
to use the package
in the current working directory.
$ go run .
Hello, world!
2 + 2: 4
Let's add division. Add the following lines under the last fmt.Println call:
quotient, err := Divide(4, 2)
if err != nil {
panic(err)
}
fmt.Println("4 / 2:", quotient)
And run it:
$ go run .
Hello, world!
2 + 2: 4
4 / 2: 2
Now, what would happen if you divide something by zero? Let's find out by adding the following after the last fmt.Println call:
quotient, err = Divide(4, 0)
if err != nil {
panic(err)
}
fmt.Println("but the quotient of 4 / 0 was:", quotient)
The :=
operator isn't used here because that would cause a compile error. This
error would happen because the last :=
already defined the variables and names
cannot be redefined in Go.
Now let's run it:
$ go run .
Hello, world!
2 + 2: 4
4 / 2: 2
panic: divide by zero
goroutine 1 [running]:
main.main()
/Users/christine.dodrill/Code/go_learning/hello/hello.go:20 +0x26c
exit status 2
This panics with the error "divide by zero" because of the call on line 20. Let's change that panic to a println so we can see what the quotient would be:
quotient, err = Divide(4, 0)
if err != nil {
fmt.Println("got an error:", err)
}
fmt.Println("but the quotient of 4 / 0 was:", quotient)
And run it:
$ go run .
Hello, world!
2 + 2: 4
4 / 2: 2
got an error: divide by zero
but the quotient of 4 / 0 was: 0
```
The error was caught. This lets you handle errors with any custom logic you'd
need to write. Sometimes it could mean passing an error up to its caller,
sometimes it could mean retrying. It really depends on the context.
## Conclusion
And that about wraps it up for Lesson 1 in Go. Next we'll be covering making a
HTTP request and validating its response with icanhazip.com. Like I mentioned
before, feedback on this helps a lot.
Thanks and be well.