Skip to content

Instantly share code, notes, and snippets.

@claritee
Last active April 13, 2020 03:20
Show Gist options
  • Save claritee/e3ef53febe278fcaf9c66dd5763f7cd3 to your computer and use it in GitHub Desktop.
Save claritee/e3ef53febe278fcaf9c66dd5763f7cd3 to your computer and use it in GitHub Desktop.
Notes on Golang

Basics

Types

https://tour.golang.org/basics/11

Declarations

  // var card string = "Ace of Spades" // type can be inferred
  card := "Ace of Spades" // := id for new variable initialisation
  card = "Five of Diamonds" // var already exists and re-assigned

Array vs Slice

Each element must have the same type

  • Array - fixed length
  • Slice - grow or shrink

Slice

cards := []string{ "Ace of Diamonds", "Queen of Hearts"}
cards = append(cards, "Sizx of Spades") // new Slice is returned, original Slice is not modified

Accessing an element

cards[index]

Ranges

cards[0:2]  // starting index : ending index => [inclusive:exclusive]
cards[:2]   // same as the above
cards[2:]   // all elements from indx 2 to the end

Using make func

bs := make([]byte, 99999) // make func - creates a byte slice with 99999 empty elements

Custom Types

type deck []string

Usage

cards := deck{"Ace of Spades", "Queen of Hearts"}

Unused Variables

_ (similar to Elixir)

Structs

Data structure with fields

https://gobyexample.com/structs

  • Pass by value (when arg into a func) - copy of struct is passed to a func. (see: notes on pointers)

Definition

(i) No attributes

type englishBot struct{}

func main() {
	eb := englishBot{}
}

(ii) with attributes

type person struct {
	firstName string
	lastName  string
}

Usage:

Ordering of fields matter

alex := person{"Alex", "Anderson"}

Being explicit

alex := person{firstName: "Alex", lastName: "Anderson"}

"Zero" values

var alex person  // default "" for both attributes

fmt.Println(alex)	// { }
fmt.Printf("%+v", alex) // {firstName: lastName:}

Assigning

alex.firstName = "Alex"
alex.lastName = "Anderson"
// {firstName:Alex lastName:Anderson}

Structs can be nested

type contactInfo struct {
	email   string
	zipCode int
}

type person struct {
	firstName string
	lastName  string
	contact   contactInfo
}

jim := person{
	firstName: "Jim",
	lastName:  "Party",
	contact: contactInfo{
		email:   "[email protected]",
		zipCode: 94000,
	},
}

Another way

type person struct {
	firstName string
	lastName  string
	contactInfo  // dont need a var name
}

contactInfo: contactInfo{
	email:   "[email protected]",
	zipCode: 94000,
},

Maps

  • Pass by reference (when arg into a func)
colors := map[string]string{
	"red":   "#ff0000",
	"green": "#4bf745",
}

Empty declaration

var colors map[string]string  // map[]
colors := make(map[string]string)

Assigning

colors["white"] = "#ffffff"

Access

colors["white"]

Deleting

delete(colors, "white")

Type Conversions

[]byte("Hi there!)  // []type(value)

Pointers

Go is a pass by value language i.e. receiver and args in a function is a copy

jim := person{ ... }

jimPointer := &jim
jimPointer.updateName("Jimmy")

func (pointerToPerson *person) updateName(newFirstName string) {
	(*pointerToPerson).firstName = newFirstName
}

Definitions

  • &variable - memory address of the variable
  • *pointer - value at the memory address

This still works (without &)

jim.updateName("Jimmy")
jim.print()

func (pointerToPerson *person) updateName(newFirstName string) {
	(*pointerToPerson).firstName = newFirstName
}

Gotcha:

Slice is a pointer to the underlying Array. This is a reference type In the following func, the slice is a copy, but still points to the same underlying Array

e.g.

func main() {
	mySlice := []string{"hi", "hello", "1", "2", "3"}
	updateSlice(mySlice)
	fmt.Println(mySlice)
}

func updateSlice(s []string) {
	s[0] = "Bye"
}

Output: [Bye hello 1 2 3] -> the original value was modified

Reference types

  • slices
  • maps
  • channels
  • pointers
  • functions

Hello World

Hello world

package main

import "fmt"

func main() {
  fmt.Println("Hi there!")
}

Run a file

go run main.go

Commands

go build    # compiles
go run      # compiles and run
go fmt      # format
go install  # compile and install package
go get      # download source code from a package
go test     # run tests

Packages and files

  • executable - generates a runnable file e.g. main.exe (main is executable)
  • reusable - "helpers" that can be re-used e.g. package name would be specialised/specific (for a lib)

Example Structure:

main package
|_ main.go 
|_ support.go
|_ helper.go

main.go

package main

func main() {
 ...
}

support.go

package main

func support() {
 ...
}

helper.go

package main

func help() {
 ...
}

Functions

func newCard() string {
	return "Five of Diamonds"
}

Receiver Functions


type deck []string

func (d deck) print() {
	for i, card := range d {
		fmt.Println(i, card)
	}
}

Usage

cards.print()

Receiver arg d is usually one letter variable abbreviation of the type

Receiver - the "object" / "instance" being called on.

Function Args and Return values

Can return more than one value

func deal(d deck, handSize int) (deck, deck) {
  ...
}

Usage:

hand, remainingCards := deal(cards, 5)

unused receiver args

// equivalent to the below, as eb is not used
func (eb englishBot) getGreeting() string {
	return "Hi There"
}

func (englishBot) getGreeting() string {
	return "Hi There"
}

Function Literal

  • Similar to a lambda or anon function
  • unnamed function with code logic
// go literal (anon func) is a go routine
go func() {
	// ...
}()
  • Note: function literal (child routine) should not reference the same variable declared in the main routine
  • Should pass the variable value - pass by value instead of reference
for l := range c {
	// go literal (anon func) is a go routine
	go func(link string) {
		time.Sleep(5 * time.Second)
		checkLink(link, c)
	}(l)
}

Loops

for i, card := range cards {
    fmt.Println(i, card)
}

When running go files when using a custom type defined in another file - must include the required files i.e.

go run main.go deck.go

Conditionals

    if err != nil {
        
    }

Interfaces

  • Implicit type - i.e. don't need to specify that a struct type does not inherit from the Interface.

Definition

Note: the receiver type on getGreeting() funcs

type bot interface {
	getGreeting() string
}

type englishBot struct{}
type spanishBot struct{}

func (englishBot) getGreeting() string {
	return "Hi There"
}

func (spanishBot) getGreeting() string {
	return "Hola!"
}

Usage:

func main() {
	eb := englishBot{}
	sb := spanishBot{}
	printGreeting(eb)
	printGreeting(sb)
}


func printGreeting(b bot) {
	fmt.Println(b.getGreeting())
}

HTTP

Request

resp, err := http.Get("http://google.com")

Error Handling

if err != nil {
	fmt.Println("Error:", err)
	os.Exit(1)
}

(1) Reading response

bs := make([]byte, 99999) // make func - creates a byte slice with 99999 empty elements
resp.Body.Read(bs)
fmt.Println(string(bs))

(2) OR using os package

io.Copy(os.Stdout, resp.Body) //arg 1: writer, arg2: reader

see: https://golang.org/pkg/io/#example_Copy

(3) Example using an interface

type logWriter struct{}
lw := logWriter{}
	io.Copy(lw, resp.Body)
func (logWriter) Write(bs []byte) (int, error) {
	fmt.Println(string(bs))
	fmt.Println("Just wrote this many bytes:", len(bs))
	return len(bs), nil
}

Misc

Random Numbers

source := rand.NewSource(time.Now().UnixNano())
r := rand.New(source)
r.Intn(10) // generate numbers from 0 - 10

Tests

Filename: xxx_test.go to test xxx.go

Run everything in the package: go test

Test names

func TestFuncName

Example

//t is the test handler
func TestNewDeck(t *testing.T) {
	d := newDeck()
	if len(d) != 16 {
		t.Errorf("Expected deck length of 16 but got %d", len(d))
	}
}

Go Routines

  • lightweight thread of execution (child go routine)
  • run async
  • Go Scheduler runs one routine until it finishes or makes a blocking call (e.g. HTTP Request).
  • Go by default uses one CPU core at a time
  • Note: Concurrency is not parallelism - i.e. Go schedule mult threads but run one at a time (one core at a time)

Create it by calling go then the function

go f(...)

Example: https://gobyexample.com/goroutines

Channels

  • Used to communicate in between different Go routines
  • Can send data into the channel from and to a Go routing
  • Data shared between routines must be of the same type (string, float, structs etc). i.e. Channel that was created sharing string cannot have another type shared.
c := make(chan string) // make a channel, string data is passed in this channel
for _, link := range links {
	go checkLink(link, c) // go routine is created and channel is passed to update
}

Note: the channel param c is of type channel accepting string types

func checkLink(link string, c chan string) {
  // ...
}

Sending and Receiving data into a channel

Send

c <- "Yep it's up"

Receive

  • Waiting for messages from a channel is blocking
fmt.Println(<- c)

Need to wait for all go routines

// infinite loop coninually checking link "l" in a channel "c"
// the link "l" is pushed into a channel and received from a channel
for l := range c {
	go checkLink(l, c)
}

Errors

Custom Errors

Definition

type MyError struct{}

func (m *MyError) Error() string {
    return "boom"
}

Usage

func sayHello() (string, error) {
    return "", &MyError{}
}

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment