Golang Intro
Golang Intro
This lesson discusses the basics of variable declaration, initialization and inferred typing.
• Variables Declaration
• Variable Initialization
There are a few concepts we are going to explore before writing our first
application.
Variables Declaration #
The var statement declares a list of variables. The name of the variable comes
first, and the type of variable is declared after it.
Environment Variables
Key: Value:
GOPATH /go
var (
name string
age int
location string
)
Or even
Environment Variables
Key: Value:
GOPATH /go
var (
name, location string
age int
)
Environment Variables
Key: Value:
GOPATH /go
Variable Initialization #
A var declaration can include initializers, one per variable.
Environment Variables
Key: Value:
GOPATH /go
var (
name string = "Prince Oberyn"
age int = 32
location string = "Dorne"
)
If an initializer is present, the type can be omitted, the variable will take the
type of the initializer (inferred typing).
Environment Variables
Key: Value:
GOPATH /go
var (
name = "Prince Oberyn"
age = 32
location = "Dorne"
)
Environment Variables
Key: Value:
GOPATH /go
var (
name, location, age = "Prince Oberyn", "Dorne", 32
)
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
name, location := "Prince Oberyn", "Dorne"
age := 32
fmt.Printf("%s age %d from %s ", name, age, location)
}
Environment Variables
Key: Value:
GOPATH /go
func main() {
action := func() { //action is a variable that contains a function
//doing something
}
action()
}
Outside a function, every construct begins with a keyword ( var , func and so
on) and the := construct is not available.
Let’s take a look at how constants are declared in the next chapter.
Constants
• Declaration
• Example
Declaration #
Constants are declared like variables, but with the const keyword.
Environment Variables
Key: Value:
GOPATH /go
const Pi = 3.14
const (
StatusOK = 200
StatusCreated = 201
StatusAccepted = 202
StatusNonAuthoritativeInfo = 203
StatusNoContent = 204
StatusResetContent = 205
StatusPartialContent = 206
)
Example #
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
const (
Pi = 3.14
Truth = false
Big = 1 << 62
Small = Big >> 61
)
func main() {
const Greeting = "ハローワールド" //declaring a constant
fmt.Println(Greeting)
fmt.Println(Pi)
fmt.Println(Truth)
fmt.Println(Big)
}
Note: The left-shift operator (<<) shifts its first operand left by the
number of bits specified by its second operand. The type of the second
operand must be an int or a type that has a predefined implicit numeric
conversion to int . The right-shift operator (>>) shifts its first operand
right by the number of bits specified by its second operand.
Now that you know how to declare constants from the above examples. Let’s
move on and read about printing constants/variables.
Printing
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
cylonModel := 6
fmt.Println(cylonModel)
}
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
name := "Caprica-Six"
aka := fmt.Sprintf("Number %d", 6)
fmt.Printf("%s is also known as %s",
name, aka) //printing variables within the string
}
Read the fmt package documentation to see the available flags to create a
format specifier.
The next lesson will touch upon packages and imports. Read on to find out
more.
Packages and Imports
• Packages
• Import statement examples
Packages #
Every Go program is made up of packages. Programs start running in package
main .
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
fmt.Printf("Hello, World!\n")
}
If you are writing an executable code (versus a library), then you need to
define a main package and a main() function which will be the entry point to
your software.
By convention, the package name is the same as the last element of the import
path. For instance, the “math/rand” package comprises files that begin with
the statement package rand.
Import statement examples #
Environment Variables
Key: Value:
GOPATH /go
import "fmt"
import "math/rand"
Or grouped:
Environment Variables
Key: Value:
GOPATH /go
import (
"fmt"
"math/rand"
)
Check out the two links below for more information about packages and
imports in GO.
Go Tour: Packages
Go Tour: Imports
Below are two examples showing how to use each of these imports.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("My favorite number is", rand.Intn(10))
}
Down below is another example to help further understand how imported
packages can be used.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("Now you have %g problems.", math.Sqrt(7))
}
Usually, nonstandard lib packages are namespaced using a web url. For
instance, there is some Rails logic ported to GO, including the cryptography
code used in Rails 4. The source code is hosted on GitHub containing a few
packages, in the following repository:
http://github.com/mattetti/goRailsYourself
To import the crypto package, you would need to use the following import
statement:
Environment Variables
Key: Value:
GOPATH /go
import "github.com/mattetti/goRailsYourself/crypto"
The next lesson further explains this in more detail. Read on to find out more!
Code Location
This lesson explains in detail how to import a code from a location using Go get command
You need to pull down the code yourself. The easiest way is to use the go get
command provided by Go.
Environment Variables
Key: Value:
GOPATH /go
$ go get github.com/mattetti/goRailsYourself/crypto
This command will pull down the code and put it in your Go path. When
installing Go, we set the GOPATH environment variable and that is what’s used
to store binaries and libraries. That’s also where you should store your code
(your workspace).
Environment Variables
Key: Value:
GOPATH /go
$ ls $GOPATH
bin pkg src
The bin folder will contain the Go compiled binaries. You should probably
add the bin path to your system path.
The pkg folder contains the compiled versions of the available libraries so the
compiler can link against them without recompiling them.
Finally the src folder contains all the Go source code organized by import
path:
Environment Variables
Key: Value:
GOPATH /go
$ ls $GOPATH/src
bitbucket.org code.google.com github.com launchpad.net
Environment Variables
Key: Value:
GOPATH /go
$ ls $GOPATH/src/github.com/mattetti
goblin goRailsYourself jet
Now that you’re done reading about import let’s learn about export in the next
chapter.
Exported names
This lesson discusses how to refer to exported names and how to use them after importing their package
• How to Export
How to Export #
After importing a package, you can refer to the names it exports (meaning
variables, methods, and functions that are available from outside of the
package). In Go, a name is exported if it begins with a capital letter. Foo is an
exported name, as is FOO . The name foo is not exported.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.pi) //lowercase name
}
As you can see the code above gave an error as lowercase cannot be exported.
On the other hand, the code below will run fine as Pi is begins with a capital
letter so can be exported.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Pi) //uppercase name
}
Pi is exported and can be accessed from outside the package, while pi isn’t
available.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"net/http"
)
func main() {
fmt.Printf("HTTP status OK uses code: %d", http.StatusOK)
}
The next lesson will cover functions and return values. Read on to find out
more!
Functions and Return values
This lesson discusses functions, return values, named results and signature
• Functions
• Return values
• Named Results
Functions #
When declaring functions, the type comes after the variable name in the
inputs.
The return type(s) are then specified after the function name and inputs,
before writing the definition. Functions can be defined to return any number
of values and each of them are put here.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
fmt.Println(add(42, 13))
}
In the following example, instead of declaring the type of each parameter, we
only declare one type that applies to both.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
fmt.Println(add(42, 13))
}
Return values #
In the following example, the location function returns two string values.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
switch city {
case "Los Angeles", "LA", "Santa Monica":
region, continent = "California", "North America"
case "New York", "NYC":
region, continent = "New York", "North America"
default:
region, continent = "Unknown", "Unknown"
}
return region, continent
}
func main() {
region, continent := location("Santa Monica")
fmt.Printf("Matt lives in %s, %s", region, continent)
}
Named Results #
Functions take parameters. In Go, functions can return multiple “result
parameters”, not just a single value. They can be named and act just like
variables.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
region, continent := location("Santa Monica")
fmt.Printf("Matt lives in %s, %s", region, continent)
}
• Go and Pointers
Go and Pointers #
Go has pointers, but no pointer arithmetic. Struct fields can be accessed
through a struct pointer. The indirection through the pointer is transparent
(you can directly call fields and methods on a pointer).
To get the pointer of a value, use the & symbol in front of the value; to
dereference a pointer, use the * symbol.
Methods are often defined on pointers and not values (although they can be
defined on both), so you will often store a pointer in a variable as in the
example below:
Environment Variables
Key: Value:
GOPATH /go
client := &http.Client{}
resp, err := client.Get("http://gobootcamp.com")
Now that you’re clear about pointers in GO. The next lesson will discuss the
concept of mutability.
Mutability
This lesson discusses mutability and how to use it with help of pointers in GO
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
me := Artist{Name: "Matt", Genre: "Electro", Songs: 42}
fmt.Printf("%s released their %dth song\n", me.Name, newRelease(me))
fmt.Printf("%s has a total of %d songs", me.Name, me.Songs)
}
As you can see the total amount of songs on the me variable’s value wasn’t
changed. To mutate the passed value, we need to pass it by reference, using a
pointer.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
me := &Artist{Name: "Matt", Genre: "Electro", Songs: 42}
fmt.Printf("%s released their %dth song\n", me.Name, newRelease(me))
fmt.Printf("%s has a total of %d songs", me.Name, me.Songs)
}
The only change between the two versions is that newRelease takes a pointer
to an Artist value and when I initialize our me variable, I used the & symbol
to get a pointer to the value.
This marks the end of this chapter. In the next chapter, we will discuss basic
types and conversions.
Quiz on Go Variables
1
Which of the following is NOT a correct way to declare a variable in Go?
2 In Go, functions can return just a single value, the “result parameter”.
5
In Go, only constants are mutable
Check Answers
Basic Types
Common Types #
Numeric types #
Example #
Given below are example declarations of variables of some of the built-in
types:
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"math/cmplx"
)
var (
goIsFun bool = true //declaring a variable of type bool
maxInt uint64 = 1<<64 - 1 //declaring a variable of type uint64
complex complex128 = cmplx.Sqrt(-5 + 12i) //declaring a variable of type complex128
)
func main() {
const f = "%T(%v)\n"
fmt.Printf(f, goIsFun, goIsFun)
fmt.Printf(f, maxInt, maxInt)
fmt.Printf(f, complex, complex)
}
Type Conversion
This lesson discusses the conversion variable types to other inbuilt types using example
• Converting Values
• Example
Converting Values #
In the previous lesson, we covered the various types a variable in Go can take.
Now, we will discuss how the type of a given variable can be changed.
Converting values from one type to another is fairly simple in Go. The
expression T(v) converts the value v to the type T .
Example #
Given below are some example numeric conversions:
C++
#include <iostream>
using namespace std;
int main() {
// your code goes here
cout << "Hello World";
return 0;
}
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
This lesson explains converting the type of a given value to another speci c value using examples
• Introduction
• Examples
• Quiz
Introduction #
In the previous lesson, we covered type conversions. Now, we will move on to
discuss how, if you have a value that you want to convert to another or a
specific type (in case of interface{} ), you can use type assertion. A type
assertion takes a value and tries to create another version in the specified
explicit type.
Examples #
In the example below, the timeMap function takes a value and if it can be
asserted as a map of interfaces{} keyed by strings, then it injects a new entry
called “updated_at” with the current time value.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"time"
)
func main() {
foo := map[string]interface{}{
"Matt": 42,
}
timeMap(foo)
fmt.Println(foo)
}
The type assertion doesn’t have to be done on an empty interface. It’s often
used when you have a function taking a param of a specific interface but the
function inner code behaves differently based on the actual object type. Here
is an example:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
s := &fakeString{"Ceci n'est pas un string"}
printString(s)
printString("Hello, Gophers")
if err != nil {
if msqlerr, ok := err.(*mysql.MySQLError); ok && msqlerr.Number == 1062 {
log.Println("We got a MySQL duplicate :(")
} else {
return err
}
}
Quiz #
Q
Which of the following is Type Assertion used for?
Check Answers
Now that we know how inbuilt types can be manipulated, we will move on to
discuss how we can create our own types in Go.
Structs
This lesson explains how to de ne new types using Structs using an example
• De nition
• Examples
• Quiz
De nition #
We previously discussed the in built types in Go, we will now move on to look
at how new types can be defined using structs. A struct is a collection of
fields/properties. You can define new types as structs or interfaces. If you are
coming from an object-oriented background, you can think of a struct to be a
light class that supports composition but not inheritance. Methods are
discussed at length in Chapter 5.
You don’t need to define getters and setters on struct fields, they can be
accessed automatically. However, note that only exported fields (capitalized)
can be accessed from outside of a package.
A struct literal sets a newly allocated struct value by listing the values of its
fields. You can list just a subset of fields by using the "Name:" syntax (the
order of named fields is irrelevant when using this syntax). The special prefix
& constructs a pointer to a newly allocated struct.
Examples #
Given below is an example struct declaration:
package main
import (
fmt
"time"
)
func main() {
fmt.Println(Bootcamp{
Lat: 34.012836,
Lon: -118.495338,
Date: time.Now(),
})
}
package main
import "fmt"
var (
p = Point{1, 2} // has type Point
q = &Point{1, 2} // has type *Point
r = Point{X: 1} // Y:0 is implicit
s = Point{} // X:0 and Y:0
)
func main() {
fmt.Println(p, q, r, s)
}
package main
import (
"fmt"
"time"
)
func main() {
event := Bootcamp{
Lat: 34.012836,
Lon: -118.495338,
}
event.Date = time.Now()
fmt.Printf("Event on %s, location (%f, %f)",
event.Date, event.Lat, event.Lon)
Quiz #
Quiz on Structs
Q
Which of the following is NOT true regarding structs?
COMPLETED 0%
1 of 1
Now that we have learnt of all the different types in Go, we can move on to
discuss how variables can be initialized.
Initializing
x := new(int)
Note that following expressions using new and an empty struct literal are
equivalent and result in the same kind of allocation/initialization:
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
)
type Bootcamp struct {
Lat float64
Lon float64
}
func main() {
x := new(Bootcamp)
y := &Bootcamp{}
fmt.Println(*x == *y)
}
Note that slices, maps and channels are usually allocated using make so the
data structure these types are built upon can be initialized.
Resources #
Allocation with new - effective Go
Composite Literals - effective Go
Allocation with make - effective Go
Now that we have gone over initializing variables in Go, we will look at how
we can use this knowledge to implement composition, the alternative to
inheritance in Go.
Composition vs Inheritance
This explains details of Composition in Go and how it can be used as an alternative to Inheritance in Go.
Composition #
Composition (or embedding) is a well understood concept for most OOP
programmers and Go supports it, here is an example of the problem it’s
solving:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
p := Player{}
p.Id = 42
p.Name = "Matt"
p.Location = "LA"
p.GameId = 90404
fmt.Printf("%+v", p) // the value in a default format when printing structs,
// the plus flag (%+v) adds field names
}
The above example demonstrates a classic OOP challenge, our Player struct
has the same fields as the User struct but it also has a GameId field. Having to
duplicate the field names isn’t a big deal, but it can be simplified by
composing our struct.
Environment Variables
Key: Value:
GOPATH /go
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
p := Player{} //initializing
p.Id = 42
p.Name = "Matt"
p.Location = "LA"
p.GameId = 90404
fmt.Printf("%+v", p)
}
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
p := Player{
User{Id: 42, Name: "Matt", Location: "LA"},
90404,
}
fmt.Printf(
"Id: %d, Name: %s, Location: %s, Game id: %d\n",
p.Id, p.Name, p.Location, p.GameId)
// Directly set a field defined on the Player struct
p.Id = 11
fmt.Printf("%+v", p)
}
When using a struct literal with an implicit composition, we can’t just pass the
composed fields. We instead need to pass the types composing the struct. Once
set, the fields are directly available.
Because our struct is composed of another struct, the methods on the User
struct is also available to the Player . Let’s define a method to show that
behavior:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
p := Player{}
p.Id = 42
p.Name = "Matt"
p.Location = "LA"
fmt.Println(p.Greetings())
}
As you can see this is a very powerful way to build data structures but it’s
even more interesting when thinking about it in the context of interfaces. By
composing one of your structure with one implementing a given interface,
your structure automatically implements the interface.
Here is another example, this time we will look at implementing a Job struct
that can also behave as a logger.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"log"
"os"
)
func main() {
job := &Job{"demo", log.New(os.Stdout, "Job: ", log.Ldate)}
// same as
// job := &Job{Command: "demo",
// Logger: log.New(os.Stderr, "Job: ", log.Ldate)}
job.Logger.Print("test")
}
Our Job struct has a field called Logger which is a pointer to another type
(log.Logger)
When we initialize our value, we set the logger so we can then call its Print
function by chaining the calls: job.Logger.Print()
But Go lets you go even further and use implicit composition. We can skip
defining the field for our logger and now all the methods available on a
pointer to log.Logger are available from our struct:
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"log"
"os"
)
func main() {
job := &Job{"demo", log.New(os.Stdout, "Job: ", log.Ldate)}
job.Print("starting now...")
}
Note that you still need to set the logger and that’s often a good reason to use a
constructor (custom constructors are used when you need to set a structure
before using a value, see Custom constructors. What is really nice with the
implicit composition is that it allows to easily and cheaply make your structs
implement interfaces. Imagine that you have a function that takes variables
implementing an interface with the Print method. By adding *log.Logger to
your struct (and initializing it properly), your struct is now implementing the
interface without you writing any custom methods.
Test Yourself! #
Quiz on Composition
Q
True or False: When using a struct literal with an implicit composition,
we can pass the composed fields.
Check Answers
That concludes the lesson on how composition works. The following section
contains an exercise based on this example to further enhance your
understanding of the concept of composition.
Quiz on Types
1
When is type assertion used?
Check Answers
Exercise on Composition
• Question
Question #
Looking at the User / Player example, you might have noticed that we
composed Player using User . This means that a Player should be able to
access methods defined in the User struct. In the code given below, add
additional code to the GreetingsForPlayer function so that it uses the
Greetings function from the User struct to print the string that the Greetings
function is printing right now:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
import "encoding/json"
This lesson explains arrays, multidimensional arrays and how to print them in GO
• Arrays
• Printing arrays
• Multi-dimensional arrays
Arrays #
The type [n]T is an array of n values of type T .
The expression:
Environment Variables
Key: Value:
GOPATH /go
var a [10]int
An array’s length is part of its type, so arrays cannot be resized. This seems
limiting, but don’t worry; Go provides a convenient way of working with
arrays.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
var a [2]string //array of size 2
a[0] = "Hello" //Zero index of "a" has "Hello"
a[1] = "World" //1st index of "a" has "World"
fmt.Println(a[0], a[1]) // will print Hello World
fmt.Println(a) // will print [Hello World]
}
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)
You can also set the array entries as you declare the array:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
a := [2]string{"hello", "world!"}
fmt.Printf("%q", a)
}
Finally, you can use an ellipsis to use an implicit length when you pass the
values:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
a := [...]string{"hello", "world!"}
fmt.Printf("%q", a)
}
Printing arrays #
Note how we used the fmt package using Printf and used the %q “verb” to
print each element quoted.
If we had used Println or the %s verb, we would have had a different result:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
a := [2]string{"hello", "world!"}
fmt.Println(a)
// [hello world!]
fmt.Printf("%s\n", a)
// [hello world!]
fmt.Printf("%q\n", a)
// ["hello" "world!"]
}
Multi-dimensional arrays #
You can also create multi-dimensional arrays:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
var a [2][3]string
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
a[i][j] = fmt.Sprintf("row %d - column %d", i+1, j+1)
}
}
fmt.Printf("%q", a)
// [["row 1 - column 1" "row 1 - column 2" "row 1 - column 3"]
// ["row 2 - column 1" "row 2 - column 2" "row 2 - column 3"]]
}
Trying to access or set a value at an index that doesn’t exist will prevent your
program from compiling, for instance, try to compile the following code:
Environment Variables
Key: Value:
GOPATH /go
package main
func main() {
var a [2]string
a[3] = "Hello"
}
That’s because our array is of length 2 meaning that the only 2 available
indexes are 0 and 1. Trying to access index 3 results in an error that tells us
that we are trying to access an index that is out of bounds since our array only
contains 2 elements and we are trying to access the 4th element of the array.
Slices, the type that we are going to see in the next lesson is more often used,
due to the fact that we don’t always know in advance the length of the array
we need.
Slices in Go
This section explains the concept of slices in Go and its details such as slicing, appending slices, nil slices and
nding slice length
• Slices
• Slicing a slice
• Syntax
• Making slices
• Appending to a slice
• Length
• Nil slices
• Resources
Slices #
Slices wrap arrays to give a more general, powerful, and convenient interface
to sequences of data. Except for items with an explicit dimension such as
transformation matrices, most array programming in Go is done with slices
rather than simple arrays.
Slices hold references to an underlying array, and if you assign one slice to
another, both refer to the same array. If a function takes a slice argument,
changes it makes to the elements of the slice will be visible to the caller,
analogous to passing a pointer to the underlying array.
A slice points to an array of values and also includes a length. Slices can be
resized since they are just a wrapper on top of another data structure.
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
p := []int{2, 3, 5, 7, 11, 13}
fmt.Println(p)
// [2 3 5 7 11 13]
}
Slicing a slice #
Slices can be re-sliced, creating a new slice value that points to the same array.
Syntax #
s[lo:hi]
s[lo:lo]
is empty and
s[lo:lo+1]
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
mySlice := []int{2, 3, 5, 7, 11, 13}
fmt.Println(mySlice)
// [2 3 5 7 11 13]
fmt.Println(mySlice[1:4])
// [3 5 7]
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names)
a := names[0:2] //slice a
b := names[1:3] //slice b
fmt.Println(a, b)
In the code above two slices, a and b are made. With a containing elements
at the index 0 and 1 of the array and b containing the elements at index 1 and
2 of the array.
Making slices #
Besides creating slices by passing the values right away (slice literal), you can
also use make . You create an empty slice of a specific length and then populate
each entry:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
cities := make([]string, 3)
cities[0] = "Santa Monica"
cities[1] = "Venice"
cities[2] = "Los Angeles"
fmt.Printf("%q", cities)
// ["Santa Monica" "Venice" "Los Angeles"]
}
It works by allocating a zeroed array and returning a slice that refers to that
array.
Appending to a slice #
Note however, that you would get a runtime error if you were to do that:
cities := []string{}
cities[0] = "Santa Monica"
As explained above, a slice is seating on top of an array, in this case, the array
is empty and the slice can’t set a value in the referred array. There is a way to
do that though, and that is by using the append function:
package main
import "fmt"
func main() {
cities := []string{}
cities = append(cities, "San Diego")
fmt.Println(cities)
// [San Diego]
}
package main
import "fmt"
func main() {
cities := []string{}
cities = append(cities, "San Diego", "Mountain View")
fmt.Printf("%q", cities)
// ["San Diego" "Mountain View"]
}
package main
import "fmt"
func main() {
cities := []string{"San Diego", "Mountain View"}
otherCities := []string{"Santa Monica", "Venice"}
cities = append(cities, otherCities...)
fmt.Printf("%q", cities)
// ["San Diego" "Mountain View" "Santa Monica" "Venice"]
}
Note that the ellipsis is a built-in feature of the language that means that the
element is a collection. We can’t append an element of type slice of strings
( []string ) to a slice of strings, only strings can be appended. However, using
the ellipsis ( ... ) after our slice, we indicate that we want to append each
element of our slice. Because we are appending strings from another slice, the
compiler will accept the operation since the types are matching.
You obviously can’t append a slice of type []int to another slice of type
[]string .
Length #
At any time, you can check the length of a slice by using len :
package main
import "fmt"
func main() {
cities := []string{
"Santa Monica",
"San Diego",
"San Francisco",
}
fmt.Println(len(cities))
// 3
countries := make([]string, 42)
fmt.Println(len(countries))
// 42
}
Nil slices #
The zero value of a slice is nil. A nil slice has a length and capacity of 0.
package main
import "fmt"
func main() {
var z []int
fmt.Println(z, len(z), cap(z))
// [] 0 0
if z == nil {
fmt.Println("nil!")
}
// nil!
}
Resources #
Next lesson discusses range form of for loops for iterating over slices. Read on
to find out more!
Range in for loops
This lesson discusses range form of for loops and their use to iterate slices in Go
• Range
• Break & continue
• Range and maps
Range #
The range form of the for loop iterates over a slice or a map. Being able to
iterate over all the elements of a data structure is very useful and range
simplifies the iteration.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
for i, v := range pow { //loops over the length of pow
fmt.Printf("2**%d = %d\n", i, v)
}
}
You can skip the index or value by assigning to _ . If you only want the index,
drop the “, value” entirely.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << uint(i)
}
for _, value := range pow {
fmt.Printf("%d\n", value)
}
}
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << uint(i)
if pow[i] >= 16 {
break //stop iterating over pow when it reaches 16
}
}
fmt.Println(pow)
// [1 2 4 8 16 0 0 0 0 0]
}
You can also skip an iteration by using continue :
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
pow := make([]int, 10)
for i := range pow {
if i%2 == 0 {
continue //skip each even index of pow
}
pow[i] = 1 << uint(i)
}
fmt.Println(pow)
// [0 2 0 8 0 32 0 128 0 512]
}
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
cities := map[string]int{
"New York": 8336697,
"Los Angeles": 3857799,
"Chicago": 2714856,
}
for key, value := range cities { //for each key-value pair in cities
fmt.Printf("%s has %d inhabitants\n", key, value)
}
}
In the code above map takes as input the string type, in this case, the name of
cities, and maps them to integers (a concept explained in the following
lesson). The loop iterates over each key , value pair in cities as range cities
goes from zero to the size of cities.
In the next lesson, we will discuss the interesting concept of maps in Go.
Maps in Go
This lesson gives an introduction to maps, using the map literals and mutating maps in Go
• Introduction
• Mutating maps
• Resources
Introduction #
Maps are somewhat similar to what other languages call “dictionaries” or
“hashes”.
A map maps keys to values. Here we are mapping string keys (actor names) to
an integer value (age).
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
celebs := map[string]int{ //mapping strings to integers
"Nicolas Cage": 50,
"Selena Gomez": 21,
"Jude Law": 41,
"Scarlett Johansson": 29,
}
fmt.Printf("%#v", celebs)
}
When not using map literals like above, maps must be created with make (not
new) before use. The nil map is empty and cannot be assigned to.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
var m map[string]Vertex
func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{40.68433, -74.39967} //assignment
fmt.Println(m["Bell Labs"])
}
In the example above the map m takes string as input and maps it to vertex
which is a struct containing two variables of type float64 . Hence, the string
“Bell” gets mapped to the value “40.68433” and “Labs” gets mapped to
“-74.39967”.
When using map literals, if the top-level type is just a type name, you can omit
it from the elements of the literal.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
// same as "Bell Labs": Vertex{40.68433, -74.39967}
"Google": {37.42202, -122.08408},
}
func main() {
fmt.Println(m)
}
Mutating maps #
Insert or update an element in map m:
m[key] = elem
Retrieve an element:
elem = m[key]
Delete an element:
delete(m, key)
elem, ok = m[key]
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
// same as "Bell Labs": Vertex{40.68433, -74.39967}
"Google": {37.42202, -122.08408},
}
func main() {
m["Splice"] = Vertex{34.05641, -118.48175} //inserting a new (key,value) here
fmt.Println(m["Splice"])
delete(m, "Splice") //deleting the element
fmt.Printf("%v\n", m)
name, ok := m["Splice"] //checks to see if element is present
fmt.Printf("key 'Splice' is present?: %t - value: %v\n", ok, name)
name, ok = m["Google"]
fmt.Printf("key 'Google' is present?: %t - value: %v\n", ok, name)
}
If key is in m , ok is true. If not, ok is false and elem is the zero value for the
map’s element type. Similarly, when reading from a map if the key is not
present the result is the zero value for the map’s element type.
Resources #
Go team blog post on maps
Effective Go - maps
This marks the end of this chapter. Read on to the next one to learn more
interesting concepts in Go.
Quiz on Collection Types
Check Answers
Slice
Check Answers
Q
Which of the following allows you to skip an iteration of a for loop?
Check Answers
Maps
Q True or False: Maps are created with the new keyword in Go.
Check Answers
Exercise on Maps
• Implement WordCount
• Question
Implement WordCount #
Have a look at this Online assignment to understand maps better before
attempting the question below.
Question #
The code below should return a map of the counts of each “word” in the string
s.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
_"strings"
"fmt"
"encoding/json"
"strconv"
)
return map[string]int{} //returning an empty map, modify the statement to return the correc
}
IF Statement
Example of an if statement:
if answer != 42 {
return "Wrong answer"
}
package main
import (
"fmt"
"math"
)
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
Example #
Example of an if and else block:
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
Now that we’ve seen the usage of if statements in Go, we will move on to
discuss for loops in Go in the next lesson.
FOR Loop
This lesson discusses for loops, including, for loop without a statement using examples and their use as an
alternative to the while loop in Go
package main
import "fmt"
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}
For Loops Without Statements #
Example of a for loop without pre/post statements:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
sum := 1
for ; sum < 1000; { //iterate as long as sum<1000
sum += sum
}
fmt.Println(sum)
}
package main
import "fmt"
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}
In nite Loops #
Infinite for loop:
package main
func main() {
for {
// do something in a loop forever
}
}
Now that we have covered the usage of for loop in Go, we will discuss
another popular control flow feature, switch statements.
Switch Case Statement
This lesson explains the key features of switch case statements and their use as an alternative to multiple if-else
statements
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now().Unix()
mins := now % 2
switch mins {
case 0:
fmt.Println("even")
case 1:
fmt.Println("odd")
}
}
Key Features #
There are a few interesting things to know about this statement in Go:
You can set an optional default statement to be executed if all the others
fail.
You can use an expression in the case statement, for instance you can
calculate a value to use in the case:
package main
import "fmt"
func main() {
num := 3
v := num % 2
switch v {
case 0:
fmt.Println("even")
case 3 - 2:
fmt.Println("odd")
}
}
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
score := 7
switch score {
case 0, 1, 3:
fmt.Println("Terrible")
case 4, 5:
fmt.Println("Mediocre")
case 6, 7:
fmt.Println("Not bad")
case 8, 9:
fmt.Println("Almost perfect")
case 10:
fmt.Println("hmm did you cheat?")
default: //to be executed if no other case matches
fmt.Println(score, " off the chart")
}
}
You can execute all the following statements after a match using the
fallthrough statement:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
n := 4
switch n {
case 0:
fmt.Println("is zero")
fallthrough //if case matches, all following conditions will be executed as w
case 1:
fmt.Println("is <= 1")
fallthrough
case 2:
fmt.Println("is <= 2")
fallthrough
case 3:
fmt.Println("is <= 3")
fallthrough
case 4:
fmt.Println("is <= 4")
fallthrough
case 5:
fmt.Println("is <= 5")
fallthrough
case 6:
fmt.Println("is <= 6")
fallthrough
case 7:
fmt.Println("is <= 7")
fallthrough
case 8:
fmt.Println("is <= 8")
fallthrough
default:
fmt.Println("Try again!")
}
}
You can use a break statement inside your matched statement to exit the
switch processing:
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"time"
)
func main() {
n := 1
switch n {
case 0:
fmt.Println("is zero")
fallthrough
case 1:
fmt.Println("<= 1")
fallthrough
case 2:
fmt.Println("<= 2")
fallthrough
case 3:
fmt.Println("<= 3")
if time.Now().Unix()%2 == 0 {
fmt.Println("un pasito pa lante maria")
break //execution stops here if this case matches i.e. no other case
}
fallthrough
case 4:
fmt.Println("<= 4")
fallthrough
case 5:
fmt.Println("<= 5")
}
}
This concludes the discussion on all the available control flow features in Go.
The following lesson contains an exercise that tests a combination of these
control flow features.
Exercise on For Loops
• Question
Question #
We have learnt that for loops are used as while loops in Go. Given below is a
C++ code that uses a while loop to sum an array of numbers. Rewrite the code
in Go so that it uses a for loop to sum a given list of numbers:
Environment Variables
Key: Value:
GOPATH /go
#include <iostream>
using namespace std;
int main() {
int foo [5] = {6, 2, 77, 4, 12};
int count=0;
int sum=0;
while (count<5){
sum+=foo[count];
count++;
}
cout<<"The sum is: "<<sum<<endl;
return 0;
}
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
import "strconv"
import "encoding/json"
IF statements
COMPLETED 0%
1 of 1
FOR Loops
Q True or False: While loops can also be used as an alternative to for loops
in Go.
COMPLETED 0%
1 of 1
COMPLETED 0%
1 of 1
Exercise on Control Flow
• Question
Question #
You have 50 bitcoins to distribute to 10 users: Matthew, Sarah, Augustus,
Heidi, Emilie, Peter, Giana, Adriano, Aaron, Elizabeth The coins will be
distributed based on the vowels contained in each name where:
and a user can’t get more than 10 coins. Print a map with each user’s name
and the amount of coins distributed. After distributing all the coins, you
should have 2 coins left.
map[Matthew:2 Peter:2 Giana:4 Adriano:7 Elizabeth:5 Sarah:2 Augustus:10 Heidi:5 Emilie:6 Aaro
Coins left: 2
Note that Go doesn’t keep the order of the keys in a map, so your results might
not look exactly the same but the key/value mapping should be the same.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
import "strconv"
import "encoding/json"
var (
coins = 50
users = []string{
"Matthew", "Sarah", "Augustus", "Heidi", "Emilie",
"Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
}
distribution = make(map[string]int, len(users))
)
fmt.Println(distribution)
fmt.Println("Coins left:", coins)
return strconv.Itoa(distribution["Matthew"])+" "+strconv.Itoa(distribution["Sarah"])+" "+st
}
Introduction
This lesson explains how methods and interfaces in Go work using an example
• Methods in Go
• Example
Methods in Go #
While technically Go isn’t an Object Oriented Programming language, types
and methods allow for an object-oriented style of programming. The big
difference is that Go does not support type inheritance (mechanism of
acquiring the features and behaviours of a class by another class) but instead
has a concept of interface.
Go does not have classes. However, you can define methods on struct types.
The method receiver appears in its own argument list between the func
keyword and the method name. Here is an example with a User struct
containing two fields: FirstName and LastName of string type.
Example #
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
)
func main() {
u := User{"Matt", "Aimonetti"}
fmt.Println(u.Greeting())
}
Note how methods are defined outside of the struct, if you have been writing
Object Oriented code for a while, you might find that a bit odd at first. The
method on the User type could be defined anywhere in the package.
Now that we know how methods and interfaces in Go work, in the following
lesson, we will look into how the code itself should be organized.
Code Organization
• Example
Example #
Methods can be defined on any file in the package, but my recommendation is
to organize the code as shown below:
Environment Variables
Key: Value:
GOPATH /go
package main
// list of constants
const (
ConstExample = "const before vars"
)
// list of variables
var (
ExportedVar = 42
nonExportedVar = "so say we all"
)
// List of functions
func NewUser(firstName, lastName string) *User {
return &User{FirstName: firstName,
LastName: lastName,
Location: &UserLocation{
City: "Santa Monica",
Country: "USA",
},
}
}
// List of methods
func (u *User) Greeting() string {
return fmt.Sprintf("Dear %s %s", u.FirstName, u.LastName)
}
func main() {
us:=User {FirstName: "Matt",
LastName: "Damon",
Location: &UserLocation{
City: "Santa Monica",
Country: "USA",}}
fmt.Println(us.Greeting())
}
In fact, you can define a method on any type you define in your package, not
just structs. You cannot define a method on a type from another package, or
on a basic type.
• Example
To define methods on a type you don’t “own”, you need to define an alias for
the type you want to extend.
Example #
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(MyStr("test").Uppercase())
}
Down below is another example explaining the concept of aliasing for better
understanding.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"math"
)
func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}
As you can see above, type aliasing declares a new name to be used as a
substitute for a previously known type. It does not introduce a new type,
neither does it change the meaning of the existing type name.
• Example
Methods can be associated with a named type ( User for instance) or a pointer
to a named type ( *User ). In the two type aliasing examples in previous lesson,
methods were defined on the value types ( MyStr and MyFloat ).
Example #
There are two reasons to use a pointer receiver. First, to avoid copying the
value on each method call (more efficient if the value type is a large struct).
The previous User example would have been better written as follows:
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
)
func main() {
u := &User{"Matt", "Aimonetti"}
fmt.Println(u.Greeting())
}
Remember that Go passes everything by value, meaning that when
Greeting() is defined on the value type, every time you call Greeting() , you
are copying the User struct. Instead when using a pointer, only the pointer is
copied (cheap).
The other reason why you might want to use a pointer is so that the method
can modify the value that its receiver points to.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"math"
)
func main() {
v := &Vertex{3, 4}
v.Scale(5)
fmt.Println(v, v.Abs())
}
In the example above, Abs() could be defined on the value type or the pointer
since the method doesn’t modify the receiver value (the vertex). However
Scale() has to be defined on a pointer since it does modify the receiver.
Scale() resets the values of the X and Y fields.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"math"
)
func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}
This marks the end of this chapter. In the next chapter, we will discuss
interfaces. Read on to find out more!
Quiz on Go Methods
1
A method is a function on an instance of an object
2 Type aliasing:
Check Answers
Introduction
This lesson gives an introduction to interfaces in Go using an example, also explains how to de ne a new type that
would implement the same interface.
• De nition
• Example
• De ning a New Type
De nition #
An interface type is defined by a set of methods. A value of interface type can
hold any value that implements those methods. Interfaces increase the
flexibility as well as the scalability of the code. Hence, it can be used to
achieve polymorphism in Golang. Interface does not require a particular type,
specifying that only some behavior is needed, which is defined by a set of
methods.
Example #
Here is a refactored version of our earlier example. This time we made the
greeting feature more generic by defining a function called Greet which takes
a param of interface type Namer . Namer is a new interface we defined which
only defines one method: Name() . So Greet() will accept as param any value
which has a Name() method defined.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
)
func main() {
u := &User{"Matt", "Aimonetti"}
fmt.Println(Greet(u))
}
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
)
func (u *User) Name() string { //Name method used for type User
return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
}
type Customer struct {
Id int
FullName string
}
func (c *Customer) Name() string { //Name method used for type Customer
return c.FullName
}
func main() {
u := &User{"Matt", "Aimonetti"}
fmt.Println(Greet(u))
c := &Customer{42, "Francesc"}
fmt.Println(Greet(c))
}
This lesson discusses implicit interfaces and how Go interfaces are satis ed.
• Implicit Interfaces
Implicit Interfaces #
A type implements an interface by implementing the methods that it contains.
package main
import (
"fmt"
"os"
)
func main() {
var w Writer
Package io has Reader and Writer defined already so you don’t have to.
This lesson talks about Displaying error messages in Go and use of fmt package for printing
package main
import (
"fmt"
"time"
)
func main() {
if err := run(); err != nil {
fmt.Println(err)
}
}
Interfaces
Errors
COMPLETED 0%
1 of 1
Exercise on Errors
• Assignment
Assignment #
Copy your Sqrt function from the earlier exercises and modify it to return an
error value. (This online assignment may be useful)
Sqrt should return a non-nil error value when given a negative number, as it
doesn’t support complex numbers.
Note: a call to fmt.Print(e) inside the Error method will send the program
into an infinite loop. You can avoid this by converting e first:
fmt.Print(float64(e)) . Why?
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"strconv"
"encoding/json"
)
//Your function should return nil for error when the number can be squared
//When the number cannot be squared, the function should return 0 along with the error
func Sqrt(x float64) (float64, error) {
//write code here
Tip: When doing an inferred declaration of a float, you can omit the decimal
value and do the following:
z := 1.
// same as
// z := 1.0
Concurrent Programming
• Introduction
Introduction #
Concurrent programming is a large topic, but it’s also one of the most
interesting aspects of the Go language.
Data races cannot occur, by design. To encourage this way of thinking we have
reduced it to a slogan:
This approach can be taken too far. Reference counts may be best done by
putting a mutex around an integer variable, for instance. But as a high-level
approach, using channels to control access makes it easier to write clear,
correct programs.
In the next lesson, we’ll introduce you to the interesting concept of goroutines,
so keep on reading!
Goroutines
This lesson introduces the concept of goroutines, their de nition, and gives an example.
• De nition
• Example
De nition #
A goroutine is a lightweight thread managed by the Go runtime. Goroutines
can be functions or methods that run concurrently with other functions or
methods
go f(x, y, z)
f(x, y, z)
Goroutines run in the same address space, so access to shared memory must
be synchronized. The sync package provides useful primitives, although you
won’t need them much in Go as there are other primitives.
Example #
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"time"
)
func main() {
go say("world") //parallel execution: both calls to say() will execute concurrently
say("hello")
}
In the next lesson, we will discuss channels used in Go. Keep on reading to
find out more!
Channels
• About channels
• Example
• Buffered channels
About channels #
Channels are pipes through which you can send and receive values using the
channel operator, <- .
Environment Variables
Key: Value:
GOPATH /go
ch := make(chan int)
By default, sends and receives block wait until the other side is ready. This
allows goroutines to synchronize without explicit locks or condition variables.
Example #
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
In the example above, all lines before line 17 will execute sequentially. Line 17
will invoke a non-blocking go-routine for the sum function and will pass the
channel c and the first half of the array in it. As the code does not block, the
function invoked by line 17 and line 18 of the main program executes
parallelly. Line 18 will take the second half of the array and the channel c as
inputs.
Hence, both the go-routines invoked in line 17 and 18 will execute in parallel.
Line 19 will not execute until we receive a value from the channels. When the
go-routines invoked by line 17 and 18 will pass values to the channels ( as
specified by c <- sum ), line 19 of the main will then execute.
Buffered channels #
Channels can be buffered. Provide the buffer length as the second argument to
make to initialize a buffered channel:
ch := make(chan int, 100)
Sends to a buffered channel block only when the buffer is full. Receives block
when the buffer is empty.
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 1
c <- 2
c <- 3
fmt.Println(<-c)
fmt.Println(<-c)
fmt.Println(<-c)
}
That’s because we overfilled the buffer without letting the code a chance to
read/remove a value from the channel.
However, this version using a goroutine would work fine:
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 1
c <- 2
c3 := func() { c <- 3 }
go c3()
fmt.Println(<-c)
fmt.Println(<-c)
fmt.Println(<-c)
}
The reason is that we are adding an extra value from inside a go routine, so
our code doesn’t block the main thread. The goroutine is being called before
the channel is being emptied, but that is fine, the goroutine will wait until the
channel is available. We then read the first value from the channel, which
frees a spot and our goroutine can push its value to the channel.
Click on Go tour page to test out the example below yourself by modifying it.
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
• Channels
• Channels as Pipes
• Sending and Receiving
• Blocking on a Send
• Blocking on a Receive
Channels #
Channels are essentially means through which Go routines communicate with
one another.
Go Routine 2
Go Routine 1
Channels in Go
Channels as Pipes #
A channel may be visualized as a pipe, through which go routines can send
and receive information from other Go routines.
Channel
Go Channels as Pipes
time
info
info
How Go Routines Communicate Through Channels
Blocking on a Send #
Once a Go routine sends information on a channel, the sending Go routine
blocks until another Go routine receives what was sent on the channel.
ZZzzZ
data
Blocking on a Receive #
Similar to blocking after sending on a channel, a Go routine can block waiting
to get a value from a channel, with nothing sent to it yet.
ZZzzZ
Now that we’ve covered the basics of Channels, the following lesson will
illustrate the differences between buffered and unbuffered channels.
Buffered vs. Unbuffered Channels as Illustrations
Illustrations of buffered and unbuffered channels to explain the differences between the two.
• Unbuffered Channels
• Buffered Channels
Unbuffered Channels #
We’ve been using unbuffered channels in all our previous illustrations. What
makes them unique is that only one piece of data fits through the channel at a
time.
One element
DATA
Unbuffered Channel
Buffered Channels #
Buffered channels work just like unbuffered channels, but with one catch —
we can send multiple pieces of data to the channel without waiting for
another Go routine to read from it.
Multiple elements
Buffered Channel
former routine doesn’t have to wait until the latter finishes, we can use a
buffered channel.
Range and close
This lesson explains the use of range and how it can be used in closing a channel in Go
• Closing a channel
• Example
Closing a channel #
A sender can close a channel to indicate that no more values will be sent.
Receivers can test whether a channel has been closed by assigning a second
parameter to the receive expression: after
v, ok := <-ch
ok is false if there are no more values to receive and the channel is closed.
The loop for i := range ch receives values from the channel repeatedly until
it is closed.
Note: Only the sender should close a channel, never the receiver.
Sending on a closed channel will cause a panic.
Another note: Channels aren’t like files; you don’t usually need to close
them. Closing is only necessary when the receiver must be told there are
no more values coming, such as to terminate a range loop.
Example #
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
)
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
As you can see in above example, the code only outputs 10 values since the
value of c passed in line 18 is 10. And you know that the forloop will only
receive values from channel till it closes, which is after the value of c is
reached in this case 10.
This lesson explains in detail the use of the select statement in Go, it's default case and concept of timeout
• Select Statement
• Example
• Default case
• Timeout
Select Statement #
The select statement lets a goroutine wait on multiple communication
operations.
A select blocks until one of its cases can run, then it executes that case. It
chooses one at random if multiple are ready.
Example #
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
Default case #
The default case in a select is run if no other case is ready.
select {
case i := <-c:
// use i
default:
// receiving from c would block
}
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
From the above example, you can see that first, the default case executes
because none of the other two cases is ready. The moment a case is ready,
such as tick case in the example above, the select command blocks till the
case is run and tick is printed.
Timeout #
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func main() {
response := make(chan *http.Response, 1)
errors := make(chan *error)
go func() {
resp, err := http.Get("http://matt.aimonetti.net/")
if err != nil {
errors <- &err
}
response <- resp
}()
for {
select {
case r := <-response:
fmt.Printf("%s", r.Body)
return
case err := <-errors:
log.Fatal(*err)
case <-time.After(200 * time.Millisecond):
fmt.Printf("Timed out!")
return
}
}
}
Note that in above example, you won’t get a response due to sandboxing.
We are using the time.After call as a timeout measure to exit if the request
didn’t give a response within 200ms.
Now that you are familiar with goroutines and their features. It’s time to
attempt a few exercise questions present in the next lesson.
Quiz on Concurrency
1
Goroutines run in the same address space?
ch <- v
4 A channel can be tested if it’s closed. However, only the receiver can
close a channel?
This section has a few questions for you try out and test your understanding of concurrency
• Assignment
• Solution
Assignment #
Online Assignment
There can be many different binary trees with the same sequence of values
stored at the leaves. For example, here are two binary trees storing the
sequence 1, 1, 2, 3, 5, 8, 13.
A function to check whether two binary trees store the same sequence is quite
complex in most languages. We’ll use Go’s concurrency and channels to write
a simple solution.
This example uses the tree package, which defines the type:
go Walk(tree.New(1), ch)
Then read and print 10 values from the channel. It should be the numbers 1 ,
2 , 3 , …, 10 .
Solution #
If you print tree.New(1) you will see the following tree:
We need to set a recursive call and for that, we are defining a non-exported
recWalk function, the function walks the left side first, then pushes the value
to the channel and then walks the right side. This allows our range to get the
values in the right order. Once all branches have been walked, we can close
the channel to indicate to the range that the walking is over.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"git://github.com/golang/tour"
"fmt"
)
// recWalk walks recursively through the tree and push values to the channel
// at each recursion
func recWalk(t *tree.Tree, ch chan int) {
if t != nil {
// send the left part of the tree to be iterated over first
recWalk(t.Left, ch)
// push the value to the channel
ch <- t.Value
// send the right part of the tree to be iterated over last
recWalk(t.Right, ch)
}
}
for {
x1, ok1 := <-ch1
x2, ok2 := <-ch2
switch {
case ok1 != ok2:
// not the same size
return false
case !ok1:
// both channels are empty
return true
case x1 != x2:
// elements are different
return false
default:
// keep iterating
}
}
}
func main() {
ch := make(chan int)
go Walk(tree.New(1), ch)
for v := range ch {
fmt.Println(v)
}
fmt.Println(Same(tree.New(1), tree.New(1)))
fmt.Println(Same(tree.New(1), tree.New(2)))
}
// recWalk walks recursively through the tree and push values to the channel
// at each recursion
func recWalk(t *tree.Tree, ch chan int) {
if t != nil {
// send the left part of the tree to be iterated over first
recWalk(t.Left, ch)
// push the value to the channel
ch <- t.Value
// send the right part of the tree to be iterated over last
recWalk(t.Right, ch)
}
}
for {
x1, ok1 := <-ch1
x2, ok2 := <-ch2
switch {
case ok1 != ok2:
// not the same size
return false
case !ok1:
// both channels are empty
return true
case x1 != x2:
// elements are different
return false
default:
// keep iterating
}
}
}
func main() {
ch := make(chan int)
go Walk(tree.New(1), ch)
for v := range ch {
fmt.Println(v)
}
fmt.Println(Same(tree.New(1), tree.New(1)))
fmt.Println(Same(tree.New(1), tree.New(2)))
}
The comparison of the two trees is trivial once we know how to extract the
values of each tree. We just need to loop through the first tree (via the
channel), read the value, get the value from the second channel (walking the
second tree) and compare the two values.
OSX
• Homebrew
• Setup your paths
• Install mercurial and bazaar
Homebrew #
The easiest way to install Go for development on OS X is to use homebrew.
In just a few minutes, go should be all installed and you should be almost
ready to code. However we need to do two small things before we start:
Before starting, we need to tell Go, where we want our workspace to be, in
other words, where our code will live. Let’s create a folder named “go” in our
home directory and set our environment to use this location.
$ mkdir $HOME/go
$ export GOPATH=$HOME/go
Note that if we open a new tab or restart our machine, Go won’t know where
to find our workspace. For that, you need to set the export in your profile:
$ open $HOME/.bash_profile
Add a new entry to set GOPATH and add the workspace’s bin folder to your
system path:
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
This will allow your Go workspace to always be set and will allow you to call
the binaries you compiled.
Official resource
• Installer
• Of cial Resource
Installer #
Install the latest version by downloading the latest installer.
Of cial Resource #
The official resource for Windows can then be used to install Go.
Linux
• Of cial Package
Of cial Package #
Install from one of the official linux packages.
• Useful Tools
• Of cial Resource
Useful Tools #
Installing Godoc , vet and Golint , three very useful Go tools from the Go
team, is highly recommended:
$ go get golang.org/x/tools/cmd/godoc
$ go get golang.org/x/tools/cmd/vet
$ go get github.com/golang/lint/golint
Of cial Resource #
The official resource for these can be useful.
Coding Challenges
A few samples of exercises you can attempt in order to further polish your skills
• Exercises
Exercises #
We have four exercises to choose from, each exercise focuses on different
challenges. Try to tackle the exercise of your choice by pairing with at least
one person, ideally, try to have four people by group.
Fork the original repo (with the instructions), work on it and send a pull
request when you are done.
Avatar me
Hashing
image manipulation
Remote commands
Copernic 2000
concurrency
consumption of web resources (http/json/XML)
sorting
data sets
data storage
Godoc API
Building a web API
Testing a web API
JSON encoding/decoding
Exploring godoc
Extending existing packages or
Shelling out
Get Your Feet Wet
• Resource
Resource #
One of the best way to learn technical skills is to actually dive in as soon as we
have acquired the basics.
This clirescue code available on GitHub was written by someone who just
started learning Go. Beginners often make the same mistakes so assume that
this is your code and you are now tasked to refactor it. The end goal is to
create a CLI to interface with the Pivotal Tracker API.
Find a one or more people and work with them to see how you would address
this refactoring. Time to rescue this project!
Tips for Importing Packages
This section will grow over time but the main goal is to share some tricks
experienced developers discovered over time. Hopefully this tips will get new
users more productive faster.
import format "fmt" - Creates an alias of fmt . Preceed all fmt package
content with format. instead of fmt. .
goimports #
Goimports is a tool that updates your Go import lines, adding missing ones
and removing unreferenced ones.
Organization #
Go is a pretty easy programming language to learn but the hardest thing for
developers at first is how to organize their code. Rails became popular for
many reasons and scaffolding was one of them. It gave new developers clear
directions and places to put their code and idioms to follow.
To some extent, Go does the same thing by providing developers with great
tools like go fmt and by having a strict compiler that won’t compile unused
variables or unused import statements.
Custom Constructors #
A question I often hear is when should I use custom constructors like NewJob .
My answer is that in most cases you don’t need to. However, whenever you
need to set your value at initialization time and you have some sort of default
values, it’s a good candidate for a constructor. In the above example, adding a
constructor makes a lot of sense so we can set a default logger.
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"log"
"os"
)
func main() {
NewJob("demo").Print("starting now...")
}
• Sets
• Dependency package management
• Using errors
Sets #
You might want to find a way to extract unique value from a collection. In
other languages, you often have a set data structure not allowing duplicates.
Go doesn’t have that built in, however it’s not too hard to implement (due to a
lack of generics, you do need to do that for most types, which can be
cumbersome).
// UniqStr returns a copy if the passed slice with only unique string results.
func UniqStr(col []string) []string {
m := map[string]struct{}{}
for _, v := range col {
if _, ok := m[v]; !ok {
m[v] = struct{}{}
}
}
list := make([]string, len(m))
i := 0
for v := range m {
list[i] = v
i++
}
return list
}
I used a few interesting tricks that are interesting to know. First, the map of
empty structs:
m := map[string]struct{}{}
We create a map with the keys being the values we want to be unique, the
associated value doesn’t really matter much so it could be anything. For
instance:
m := map[string]bool{}
if _, ok := m[v]; !ok {
m[v] = struct{}{}
}
What we are doing here, is simply check if there is a value associated with the
key v in the map m , we don’t care about the value itself, but if we know that
we don’t have a value, then we add one.
Once we have a map with unique keys, we can extract them into a new slice of
strings and return the result.
Here is the test for this function, as you can see, I used a table test, which is
the idiomatic Go way to run unit tests:
Environment Variables
Key: Value:
GOPATH /go
Given below is the implementation of the UniqueStr() method and the test
code that logs a runtime error if the output from the method does not match
the test cases:
Environment Variables
Key: Value:
GOPATH /go
package main
import (
"log"
"reflect"
)
// UniqStr returns a copy if the passed slice with only unique string results.
func UniqStr(col []string) []string {
m := map[string]struct{}{}
for _, v := range col {
_, ok := m[v]
if !ok {
m[v] = struct{}{}
}
}
list := make([]string, len(m))
i := 0
for v := range m {
list[i] = v
i++
}
return list
}
func main() {
data := []struct{ in, out []string }{
{[]string{}, []string{}},
{[]string{"", "", ""}, []string{""}},
{[]string{"a", "a"}, []string{"a"}},
{[]string{"a", "b", "a"}, []string{"a", "b"}},
{[]string{"a", "b", "a", "b"}, []string{"a", "b"}},
{[]string{"a", "b", "b", "a", "b"}, []string{"a", "b"}},
{[]string{"a", "a", "b", "b", "a", "b"}, []string{"a", "b"}},
{[]string{"a", "b", "c", "a", "b", "c"}, []string{"a", "b", "c"}},
}
for _, exp := range data {
res := UniqStr(exp.in)
if !reflect.DeepEqual(res, exp.out) {
The challenge is that if you have multiple developers on your project, you
want all of them to be on the same version of your dependencies. Your
dependencies might also have their own dependencies and you want to make
sure everything is in a good state. It gets even tricker when you have multiple
projects using different versions of the same dependency. This is typically the
case in a CI environment.
When switching to a different project, we run the project gpm script to pull
down or set the right revision of each package.
Using errors #
Errors are very important pattern in Go and at first, new developers are
surprised by the amount of functions returning a value and an error.
Go doesn t have a concept of an exception like you might have seen in other
programming languages. Go does have something called panic but as its
name suggests they are really exceptional and shouldn’t be rescued (that said,
they can be).
Andrew Gerrand from the Go team wrote a great blog post on errors I strongly
recommend you read it.
• Compiler Optimizations
• Expvar
• Setting the Build ID using git’s SHA
• How to see what packages my app imports
Compiler Optimizations #
You can pass specific compiler flags to see what optimizations are being
applied as well as how some aspects of memory management. This is an
advanced feature, mainly for people who want to understand some of the
compiler optimizations in place.
package main
import "fmt"
func main() {
u := NewUser(42, "Matt", "LA")
fmt.Println(u.Greetings())
}
The compiler notices that it can inline the NewUser function defined on line 15
and inline it on line 21. Dave Cheney has a great post about why Go’s inlining
is helping your programs run faster.
Basically, the compiler moves the body of the NewUser function (L15) to where
it’s being called (L21) and therefore avoiding the overhead of a function call
but increasing the binary size.
func main() {
id := 42 + 1
u := &User{id, "Matt", "LA"}
fmt.Println(u.Greetings())
}
On a few lines, you see the potentially alarming leaking param message. It
doesn’t mean that there is a memory leak but that the param is kept alive
even after returning. The “leaked params” are:
“leaked” because they are used in the User 's literal value. Note that id
doesn’t leak because it’s a value, only references and pointers can leak.
X argument does not escape means that the argument doesn’t “escape” the
function, meaning that it’s not used outside of the function so it’s safe to store
it on the stack.
On the other hand, you can see that &User literal escapes to heap . What it
means is that the address of a literal value is used outside of the function and
therefore can’t be stored on the stack. The value could be stored on the stack,
except a pointer to the value escapes the function, so the value has to be
moved to the heap to prevent the pointer referring to incorrect memory once
the function returns. This is always the case when calling a method on a value
and the method uses one or more fields.
Expvar #
TODO package
The next step is to set an exported variable that you will set at compilation
time using the -ldflags flag.
Environment Variables
Key: Value:
GOPATH /go
package main
import "fmt"
// compile passing -ldflags "-X main.Build <build sha1>"
var Build string
func main() {
fmt.Printf("Using build: %s\n", Build)
}
Save the above code in a file called example.go . If you run the above code,
Build won’t be set, for that you need to set it using go build and the -
ldflags .
$ ./example
Using build: a1064bc
Now, hook that into your deployment compilation process, I personally like
Rake to do that, and this way, every time I compile, I think of Jim Weirich.
It’s often practical to see what packages your app is importing. Unfortunately
there isn’t a simple way to do that, however it is doable via the go list tool
and using templates.
$ cd $GOPATH/src/github.com/GoBootcamp/clirescue
$ go list -f '{{join .Deps "\n"}}' |
xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}'
github.com/GoBootcamp/clirescue/cmdutil
github.com/GoBootcamp/clirescue/trackerapi
github.com/GoBootcamp/clirescue/user
github.com/codegangsta/cli
If you want the list to also contain standard packages, edit the template and
use:
const (
CCVisa = "Visa"
CCMasterCard = "MasterCard"
CCAmericanExpress = "American Express"
)
At other times, we only care to distinguish one thing from the other. There are
times when there’s no inherently meaningful value for a thing. For example, if
we’re storing products in a database table we probably don’t want to store
their category as a string. We don’t care how the categories are named, and
besides, marketing changes the names all the time.
const (
CategoryBooks = 0
CategoryHealth = 1
CategoryClothing = 2
)
Instead of 0, 1, and 2 we could have chosen 17, 43, and 61. The values are
arbitrary.
Constants are important but they can be hard to reason about and difficult to
maintain. In some languages like Ruby developers often just avoid them. In
Go, constants have many interesting subtleties that, when used well, can make
the code both elegant and maintainable.
Auto Increment #
A handy idiom for this in golang is to use the iota identifier, which simplifies
constant definitions that use incrementing numbers, giving the categories
exactly the same values as above.
const (
CategoryBooks = iota // 0
CategoryHealth // 1
CategoryClothing // 2
)
Custom Types #
Auto-incrementing constants are often combined with a custom type, allowing
you to lean on the compiler.
const (
TypicalNoob Stereotype = iota // 0
TypicalHipster // 1
TypicalUnixWizard // 2
TypicalStartupFounder // 3
)
func main() {
n := TypicalHipster
fmt.Println(CountAllTheThings(n)) // cannot use TypicalHipster (type Stereotype) as type
}
func main() {
i := 2
fmt.Println(SoSayethThe(i)) // cannot use i (type int) as type Stereotype in argument to
}
There’s a dramatic twist, however. You could pass a number constant, and it
would work:
func main() {
fmt.Println(SoSayethThe(0))
}
// output:
// I'm a confused ninja rockstar.
This is because constants in Go are loosely typed until they are used in a strict
context.
Skipping Values #
Imagine that you’re dealing with consumer audio output. The audio might not
have any output whatsoever, or it could be mono, stereo, or surround.
On the one hand, it’s 6 channel output, but on the other hand, only 5 of those
channels are full bandwidth channels (hence the 5.1 designation – with the .1
referring to the low-frequency effects channel).
const (
OutMute AudioOutput = iota // 0
OutMono // 1
OutStereo // 2
_
_
OutSurround // 5
)
Expressions #
The iota can do more than just increment. Or rather, iota always increments,
but it can be used in expressions, storing the resulting value in the constant.
const (
IgEggs Allergen = 1 << iota // 1 << 0 which is 00000001
IgChocolate // 1 << 1 which is 00000010
IgNuts // 1 << 2 which is 00000100
IgStrawberries // 1 << 3 which is 00001000
IgShellfish // 1 << 4 which is 00010000
)
This works because when you have only an identifier on a line in a const
group, it will take the previous expression and reapply it, with the
incremented iota. In the language of the spec, this is called implicit repetition
of the last non-empty expression list.
If you’re allergic to eggs, chocolate, and shellfish, and flip those bits to the “on”
position (mapping the bits right to left), then you get a bit value of 00010011,
which corresponds to 19 in decimal.
// output:
// 19
const (
_ = iota // ignore first value by assigning to blank identifier
KB ByteSize = 1 << (10 * iota) // 1 << (10*1)
MB // 1 << (10*2)
GB // 1 << (10*3)
TB // 1 << (10*4)
PB // 1 << (10*5)
EB // 1 << (10*6)
ZB // 1 << (10*7)
YB // 1 << (10*8)
)
What happens if you define two constants on the same line? What is the value
of Banana? 2 or 3? And what about Durian?
const (
Apple, Banana = iota + 1, iota + 2
Cherimoya, Durian
Elderberry, Fig
)
The iota increments on the next line, rather than as soon as it gets referenced.
// Apple: 1
// Banana: 2
// Cherimoya: 2
// Durian: 3
// Elderberry: 3
// Fig: 4
Which is messed up, because now you have constants with the same value.
So, Yeah
There’s a lot more to be said about constants in Go, and you should probably
read Rob Pike’s blog post on the subject over on the golang blog.
Web resources #
Dave Cheney maintains a list of resources for new Go developers.