Introduction
Go, also known as Golang, is a programming language created by Google. It is an open-source language known for its exceptional performance and efficiency. It is ideal for building server-side software, developer tools, and web applications. Due to its simplicity, Go is becoming increasingly popular for creating scalable and efficient web applications and microservices.
Useful Links
Hello World!
package main
import "fmt"
func main() {
message := greetMe("world")
fmt.Println(message)
}
func greetMe(name string) string {
return "Hello, " + name + "!"
}
$ go build
Variables
var msg string
var msg = "Hello, world!"
var msg string = "Hello, world!"
var x, y int
var x, y int = 1, 2
var x, msg = 1, "Hello, world!"
msg = "Hello"
// Declaration with list
var (
x int
y = 20
z int = 30
d, e = 40, "Hello"
f, g string
)
msg := "Hello"
x, msg := 1, "Hello"
Constants
Constants can take numerical, string, logical, or numeric values.
const Phi = 1.618
const Size int64 = 1024
const x, y = 1, 2
const (
Pi = 3.14
E = 2.718
)
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
Basic Data Types
String
str := "Hello"
str := `Multiline
string`
Number
num := 3 // int
num := 3. // float64
num := 3 + 4i // complex128
num := byte('a') // byte (alias for uint8)
Other Numeric Types
var u uint = 7 // uint (unsigned)
var p float32 = 22.7 // 32-bit float
Arrays
Arrays have a defined size.
// var numbers [5]int
numbers := [...]int{0, 0, 0, 0, 0}
Slices
Slices have dynamic size, unlike arrays.
slice := []int{2, 3, 4}
slice := []byte("Hello")
Pointers
Pointers point to the memory location of a variable.
func main() {
b := *getPointer()
fmt.Println("Value is", b)
}
func getPointer() (myPointer *int) {
a := 234
return &a
}
a := new(int)
*a = 234
Type Conversions
i := 2
f := float64(i)
u := uint(i)
Flow Control
Conditions
if day == "sunday" || day == "saturday" {
rest()
} else if day == "monday" && isTired() {
groan()
} else {
work()
}
Statements in if
A condition in an if statement can be preceded by a statement before the semicolon. Variables declared in this statement are only in scope until the end of the if statement.
if _, err := doThing(); err != nil {
fmt.Println("Uh oh")
}
Switch Statement
switch day {
case "sunday":
// Cases don't "fall through" by default!
fallthrough
case "saturday":
rest()
default:
work()
}
For Loop
for count := 0; count <= 10; count++ {
fmt.Println("My counter is at", count)
}
For-Range Loop
entry := []string{"Jack", "John", "Jones"}
for i, val := range entry {
fmt.Printf("At position %d, the character %s is present\n", i, val)
}
While Loop
n := 0
x := 42
for n != x {
n := guess()
}
Functions
In Go, functions are first-class objects.
Lambdas
myfunc := func() bool {
return x > 10000
}
Multiple Return Values
a, b := getMessage()
func getMessage() (a string, b string) {
return "Hello", "World"
}
Named Return Values
By defining named return values in a
function signature, returning without arguments will return the variables with those names.
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
Packages
Imports
import "fmt"
import "math/rand"
import (
"fmt" // gives fmt.Println
"math/rand" // gives rand.Intn
)
Both cases are equivalent.
Aliases
import r "math/rand"
r.Intn()
Packages
Every package file must start with the ‘package’ keyword.
package hello
Exported Names
Exported names start with capital letters.
func Hello() {
···
}
Concurrency
Goroutines
Channels are concurrency-safe communication objects used in Goroutines.
func main() {
// A "channel"
ch := make(chan string)
// Start concurrent routines
go push("Moe", ch)
go push("Larry", ch)
go push("Curly", ch)
// Read 3 results
// (Since our goroutines are concurrent,
// the order isn't guaranteed!)
fmt.Println(<-ch, <-ch, <-ch)
}
func push(name string, ch chan string) {
msg := "Hey, " + name
ch <- msg
}
Buffered Channels
Buffered channels limit the amount of messages they can hold.
ch := make(chan int, 2)
ch <- 1
ch <- 2
ch <- 3
// fatal error:
// all goroutines are asleep - deadlock!
Closing Channels
ch <- 1
ch <- 2
ch <- 3
close(ch)
Iterates across a channel until it’s closed.
for i := range ch {
···
}
Closed if ok == false.
v, ok := <-ch
WaitGroup
import "sync"
func main() {
var wg sync.WaitGroup
for _, item := range itemList {
// Increment WaitGroup Counter
wg.Add(1)
go doOperation(&wg, item)
}
// Wait for goroutines to finish
wg.Wait()
}
func doOperation(wg *sync.WaitGroup, item string) {
defer wg.Done()
// do operation on item
// ...
}
WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. The goroutine calls wg.Done() when it finishes. See: WaitGroup.
Error Control
Defer
Defer runs a function until the surrounding function returns. The arguments are evaluated immediately, but the function call is delayed until later.
func main() {
defer fmt.Println("Done")
fmt.Println("Working...")
}
Deferring Functions
func main() {
defer func() {
fmt.Println("Done")
}()
fmt.Println("Working...")
}
Lambdas (anonymous functions) are better suited for defer blocks.
func main() {
var d = int64(0)
defer func(d *int64) {
fmt.Printf("& %v Unix Sec\n", *d)
}(&d)
fmt.Print("Done ")
d = time.Now().Unix()
}
The defer func uses the current value of d, unless we use a pointer to get the final value at the end of the main.
Structs
Defining
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X, v.Y)
}
Literals
v := Vertex{X: 1, Y: 2}
// Field names can be omitted
v := Vertex{1, 2}
// Y is implicit
v := Vertex{X: 1}
Using Pointers with Structs
v := &Vertex{1, 2}
v.X = 2
Doing v.X is the same as doing (*v).X when v is a pointer.
Methods
Receivers
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X * v.X + v.Y * v.Y)
}
v := Vertex{1, 2}
v.Abs()
There are no classes, but you can define functions with receivers.
Mutations
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
v := Vertex{6, 12}
v.Scale(0.5)
// `v` is updated
By defining your receiver as a pointer (*Vertex), you can do mutations.
Interfaces
Basic Interface
type Shape interface {
Area() float64
Perimeter() float64
}
Struct
type Rectangle struct {
Length, Width float64
}
The Rectangle struct implicitly implements the Shape interface by implementing all of its methods.
Methods
func (r Rectangle) Area() float64 {
return r.Length * r.Width
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Length + r.Width)
}
The methods defined in the Shape interface are implemented in the Rectangle.
Example of an Interface
func main() {
var r Shape = Rectangle{Length: 3, Width: 4}
fmt.Printf("Type of r: %T, Area: %v, Perimeter: %v.", r, r.Area(), r.Perimeter())
}