388 lines
10 KiB
Markdown
388 lines
10 KiB
Markdown
|
---
|
|||
|
sitename: Within
|
|||
|
title: "Go Training 1: Get Going"
|
|||
|
template: blog
|
|||
|
---
|
|||
|
|
|||
|
This post is the first in a series I'm creating to help people learn the
|
|||
|
[Go][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.
|
|||
|
|
|||
|
[go]: https://golang.org
|
|||
|
|
|||
|
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:
|
|||
|
|
|||
|
```go
|
|||
|
func Add(x int, y int) int {
|
|||
|
return x + y
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
This function wraps [integer
|
|||
|
addition](https://golang.org/ref/spec#Arithmetic_operators). 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](https://www.digitalocean.com/community/tutorials/how-to-install-go-on-ubuntu-18-04)
|
|||
|
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:
|
|||
|
|
|||
|
```console
|
|||
|
$ 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](https://github.com/golang/go/wiki/SettingGOPATH#windows).
|
|||
|
|
|||
|
## 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:
|
|||
|
|
|||
|
```go
|
|||
|
// 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:
|
|||
|
|
|||
|
```go
|
|||
|
// 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:
|
|||
|
|
|||
|
```console
|
|||
|
$ 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`:
|
|||
|
|
|||
|
```console
|
|||
|
$ 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](https://github.com/golang/go/wiki/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:
|
|||
|
|
|||
|
```console
|
|||
|
$ 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:
|
|||
|
|
|||
|
```console
|
|||
|
$ 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`:
|
|||
|
|
|||
|
```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:
|
|||
|
|
|||
|
```go
|
|||
|
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.
|
|||
|
|
|||
|
```go
|
|||
|
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:
|
|||
|
|
|||
|
```go
|
|||
|
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.
|
|||
|
|
|||
|
```console
|
|||
|
$ go run .
|
|||
|
Hello, world!
|
|||
|
2 + 2: 4
|
|||
|
```
|
|||
|
|
|||
|
Let's add division. Add the following lines under the last fmt.Println call:
|
|||
|
|
|||
|
```go
|
|||
|
quotient, err := Divide(4, 2)
|
|||
|
if err != nil {
|
|||
|
panic(err)
|
|||
|
}
|
|||
|
fmt.Println("4 / 2:", quotient)
|
|||
|
```
|
|||
|
|
|||
|
And run it:
|
|||
|
|
|||
|
```console
|
|||
|
$ 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:
|
|||
|
|
|||
|
```go
|
|||
|
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:
|
|||
|
|
|||
|
```console
|
|||
|
$ 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:
|
|||
|
|
|||
|
```go
|
|||
|
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:
|
|||
|
|
|||
|
```console
|
|||
|
$ 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.
|