Skip to content

Instantly share code, notes, and snippets.

@harpresing
Last active June 22, 2016 18:17
Show Gist options
  • Save harpresing/4acd0bb8080740526cfdccb75a93bae4 to your computer and use it in GitHub Desktop.
Save harpresing/4acd0bb8080740526cfdccb75a93bae4 to your computer and use it in GitHub Desktop.

Introduction

Go is a compiled language, statically typed language, with the runtime built into the applications since it does not have a Virtual Machine like Java. It is based mostly on C, but influenced by other languages.

Go implements many OOP concepts:

  • Encapsulation with types and structs
  • Polymorphism by Interfaces = Contracts that can be implemented by interfaces and types
  • Custom types = Implement one or more interfaces
  • Custom types have custom methods
  • Custom structs(data structures) can have member fields

What go doesn't support:

  • No type inheritance (no classes)
  • Method or Operator Overloading
  • Structured Exception Handling
  • Implicit Numeric Conversions

Essential Syntax Rules:

  • Go is case sensitive
  • Exported functions and fields have an initial upper case character which signifies that it is available to the rest of the application i.e. it is public
  • Code blocks wrapped with braces {} -Starting brace should be at the same line as the preceding statement
  • No semicolons required

Other Characteristics:

  • the builtin package is imported to every goroutine by default, it has the following members:
  • len(string)
  • panic(error) - stops execution and displays error message
  • recover() - manages behaviour of panicking goroutine

First go program:

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println("Hello lodu")
	fmt.Println(strings.ToUpper("Hello lodu"))
}

Package names should be all lowercase and a single word. Go commands:

  • go doc package_name - gives info about a package
  • go run file.go - executes a file
  • go install - places the executables and the packages in the bin/ and pkg/ folders
  • go build - creates an executable
fmt package

Format package lets you play around with strings in a variety of ways. Go functions are allowed to return more than one value simultaneously.

fmt.Println = returns the string length and if any error was thrown simultanously (an integer and an error object)

package main

import (
	"fmt"
)

func main() {
	str1 := "The quick fox jumped over"
	str2 := "the lazy brown dog"
	isNum := 42
	isBool := true
	stringLength, err := fmt.Println(str1, str2) 
	// , delimiter automatically adds a space just like in python2.7
	
	if err == nil {
		fmt.Println("the length is", stringLength)
	}
	fmt.Printf("Printing a number: %v\nPrinting a boolean: %v\n", isNum, isBool)
	// %v prints the value of the variable, it's like a verb inserted in the string
	fmt.Printf("Printing a number: %.2f\n", float64(isNum)) //since there is no implicit conversion
	// .2f = two decimal points
	fmt.Printf("Data types: %T, %T, %T and %T\n", str1, str2, isNum, isBool) // prints the data type
	myString = fmt.Sprintf("Data types: %T, %T, %T and %T ", str1, str2, isNum, isBool)
	fmt.Println(myString)
}
Inputting stuff from the console
package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	// var s string
	// fmt.Scanln(&s) // Doesn't read anything after a space
	// fmt.Println(s)
	reader := bufio.NewReader(os.Stdin) //read from console keyboard
	fmt.Print("Enter input")
	str, _ := reader.ReadString('\n') // read the string till it finishes at newline
	// err = _ when you don't want to deal with it
	fmt.Println(str)

	// Now parsing a number is done via strconv package
	fmt.Print("Enter a number:")
	str, _ = reader.ReadString('\n') //since str is already declared so no colon
	f, err := strconv.ParseFloat(strings.TrimSpace(str), 64)

	if err == nil {
		fmt.Println("Value entered", f)
	} else {
		fmt.Println(err)
	}
}

Declaring Variables

Variables are static, their type can be defined implicitly or explicitly as:

  • Explicit type :- var aString string = "hello world"
    • They dont need to be initialized
  • Implicit type (inferred) :- aString := "hello world" Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type.

Outside a function, every statement begins with a keyword (var, func, and so on) and so the := construct is not available.

Predefined Complex types

Data types:- Arrays, Slices, Maps and Structs

Language Organization:- Functions, Interfaces and Channels

Data Management:- Pointers

import (
   "strings"
)
  • strings.ToUpper(str)
  • strings.Title(str) - first letter of every word is capital
  • string conparison can be done with ==, is case-sensitive
  • strings.EqualFold(str1, str2) - Comparison case-insensitive
  • strings.Contains(str, "substring") - returns boolean

Loops

for loop:

sum := 0
	for i := 0; i < 10; i++ {
		sum += i
	}

while loop:

sum := 0
	for sum < 10 {
	    sum += 1
	}

If statements can also have initialization, like for, the if statement can start with a short statement to execute before the condition.

Variables declared by the statement are only in scope until the end of the if.

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	}
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

Use newtons method to find square roots:

package main

import (
	"fmt"
)

func Sqrt(x float64) float64 {
	del :=  0.0000001
	srt, prevSrt := x, x * 2
	for prevSrt - srt > del  {
		prevSrt = srt
		srt = srt - (srt*srt - x)/(2 * srt)
		//fmt.Printf("Difference of prev - srt = %v\n", prevSrt-srt)
		}
	return srt
}

func main() {
	fmt.Println(Sqrt(25))
}

Defer

A defer statement defers the execution of a function until the surrounding function returns.

The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.

Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order.

package main

import "fmt"

func main() {
	defer fmt.Println("world")

	fmt.Println("hello")
}

Pointers

The type *T is a pointer to a T value. Its zero value is nil. var p *int The & operator generates a pointer to its operand. The * operator denotes the pointer's underlying value.

package main

import "fmt"

func main() {
	i, j := 42, 2701

	p := &i         // point to i
	fmt.Println(*p) // read i through the pointer
	*p = 21         // set i through the pointer
	fmt.Println(i)  // see the new value of i

	p = &j         // point to j
	*p = *p / 37   // divide j through the pointer
	fmt.Println(j) // see the new value of j
}

Exercise: Slices Implement Pic. It should return a slice of length dy, each element of which is a slice of dx 8-bit unsigned integers. When you run the program, it will display your picture, interpreting the integers as grayscale (well, bluescale) values.

The choice of image is up to you. Interesting functions include (x+y)/2, x*y, and x^y.

(You need to use a loop to allocate each []uint8 inside the [][]uint8.)

(Use uint8(intValue) to convert between types.)

package main

import (
	"golang.org/x/tour/pic"
)

func Pic(dx, dy int) [][]uint8 {
	b := make([][]uint8, dy)
	for i:= range b{
		b[i] = make([]uint8, dx)
		for j:=range b[i]{
		b[i][j] = uint8((i*j)/10)
		} 
	}
	return b
}

func main() {
	pic.Show(Pic)
}

Exercise: Maps Implement WordCount. It should return a map of the counts of each “word” in the string s. The wc.Test function runs a test suite against the provided function and prints success or failure.

You might find strings.Fields helpful

package main

import (
	"golang.org/x/tour/wc"
	"strings"
)

func WordCount(s string) map[string]int {
	var ok bool
	count := make(map[string]int)
	words := strings.Fields(s)
	for _, word:= range words{
		_, ok = count[word]
		if ok == false { 
			count[word] = 1
		}else { 
			count[word] += 1 
		}
	}
	return count
}

func main() {
	wc.Test(WordCount)
}

Function values Functions are values too. They can be passed around just like other values.

Function values may be used as function arguments and return values.

package main

import (
	"fmt"
	"math"
)

func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

func main() {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	fmt.Println(hypot(5, 12))

	fmt.Println(compute(hypot))
	fmt.Println(compute(math.Pow))
}

Function closures Go functions may be closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables.

For example, the adder function returns a closure. Each closure is bound to its own sum variable.

package main

import "fmt"

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}

Exercise: Fibonacci closure Let's have some fun with functions.

Implement a fibonacci function that returns a function (a closure) that returns successive fibonacci numbers (0, 1, 1, 2, 3, 5, ...).

My Solution:

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
	f := 0
	var fprev, temp int
	return func() int{
		if f == 0{
			fprev = 0
			f = 1
			return fprev
		}else {
			temp = f 
			f += fprev  
			fprev = temp 
		}
		return f
	}
}

func main() {
	f := fibonacci()
	for i := 0; i < 20; i++ {
		if i == 2{
		fmt.Println(1)
		}
		fmt.Println(f())
	}
}

Better Solution

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
	x := 0
	y := 1
	return func() int{
		x, y = y, x+y
		return y
	}
}

func main() {
	f := fibonacci()
	fmt.Println("0\n1")
	for i := 0; i < 20; i++ {
		fmt.Println(f())
	}
}

Go Methods

A method is a function with a special receiver argument. The receiver appears in its own argument list between the func keyword and the method name. In this example, the Abs method has a receiver of type Vertex named v.

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
} // the previous function can also be written normally as

/* func Abs(v Vertex) float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
} */


func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Abs())
	// In the second instance, without the reciever, you'd call the function as Abs(v)
}

You can declare a method on non-struct types, too.

In this example we see a numeric type MyFloat with an Abs method.

package main

import (
	"fmt"
	"math"
)

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

func main() {
	f := MyFloat(-math.Sqrt2)
	fmt.Println(f.Abs())
}

Pointer receivers

You can declare methods with pointer receivers.

This means the receiver type has the literal syntax *T for some type T. (Also, T cannot itself be a pointer such as *int.)

For example, the Scale method here is defined on *Vertex.

Methods with pointer receivers can modify the value to which the receiver points (as Scale does here). Since methods often need to modify their receiver, pointer receivers are more common than value receivers.

Try removing the * from the declaration of the Scale function on line 16 and observe how the program's behavior changes.

With a value receiver, the Scale method operates on a copy of the original Vertex value. (This is the same behavior as for any other function argument.) The Scale method must have a pointer receiver to change the Vertex value declared in the main function.

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func main() {
	v := Vertex{3, 4}
	v.Scale(10)
	fmt.Println(v.Abs())
}

Methods and pointer indirection Comparing the previous two programs, you might notice that functions with a pointer argument must take a pointer:

var v Vertex ScaleFunc(v) // Compile error! ScaleFunc(&v) // OK while methods with pointer receivers take either a value or a pointer as the receiver when they are called:

var v Vertex v.Scale(5) // OK p := &v p.Scale(10) // OK For the statement v.Scale(5), even though v is a value and not a pointer, the method with the pointer receiver is called automatically. That is, as a convenience, Go interprets the statement v.Scale(5) as (&v).Scale(5) since the Scale method has a pointer receiver.

package main

import "fmt"

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func ScaleFunc(v *Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func main() {
	v := Vertex{3, 4}
	v.Scale(2)
	ScaleFunc(&v, 10)

	p := &Vertex{4, 3}
	p.Scale(3)
	ScaleFunc(p, 8)

	fmt.Println(v, p)
}

Choosing a value or pointer receiver

There are two reasons to use a pointer receiver.

The first is so that the method can modify the value that its receiver points to.

The second is to avoid copying the value on each method call. This can be more efficient if the receiver is a large struct, for example.

In this example, both Scale and Abs are with receiver type *Vertex, even though the Abs method needn't modify its receiver.

In general, all methods on a given type should have either value or pointer receivers, but not a mixture of both. (We'll see why over the next few pages.)

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := &Vertex{3, 4}
	fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs())
	v.Scale(5)
	fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs())
}

Essentially if a function has a reciever value before the function name, it acts like call by reference in java, and it is called in the same fashion as you use objects to call a function in Java, Eg v.Abs()


Interfaces

An interface type is defined as a set of method signatures.

A value of interface type can hold any value that implements those methods.

Note: There is an error in the example code on line 39. Vertex (the value type) doesn't implement Abser because the Abs method is defined only on *Vertex (the pointer type).

package main

import (
	"fmt"
	"math"
)

type MyFloat float64

type Abser interface {
	Abs() float64
}

type Vertex struct {
	X, Y float64
}

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	var a Abser
	f := MyFloat(-math.Sqrt2)
	v := Vertex{3, 4}

	a = f  // a MyFloat implements Abser
	a = &v // a *Vertex implements Abser

	// In the following line, v is a Vertex (not *Vertex)
	// and does NOT implement Abser.
	a = v

	fmt.Println(a.Abs())
}

The empty interface

The interface type that specifies zero methods is known as the empty interface:

interface{} An empty interface may hold values of any type. (Every type implements at least zero methods.)

Empty interfaces are used by code that handles values of unknown type. For example, fmt.Print takes any number of arguments of type interface{}.

package main

import "fmt"

func main() {
	var i interface{}
	describe(i)

	i = 42
	describe(i)

	i = "hello"
	describe(i)
}

func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i)
}

Output

(<nil>, <nil>)
(42, int)
(hello, string)

Interface Type Assertions

package main

import "fmt"

func main() {
	var i interface{} = "hello"

	s := i.(string)
	fmt.Println(s)

	s, ok := i.(string)
	fmt.Println(s, ok)

	f, ok := i.(float64)
	fmt.Println(f, ok)

	f = i.(float64) // panic
	fmt.Println(f)
}

Output

hello
hello true
0 false
panic: interface conversion: interface is string, not float64

goroutine 1 [running]:
panic(0x13f560, 0x10430200)
	/usr/local/go/src/runtime/panic.go:481 +0x700
main.main()
	/tmp/sandbox511704234/main.go:17 +0x4e0

Stringers

One of the most ubiquitous interfaces is Stringer defined by the fmt package.

type Stringer interface {
    String() string
}

A Stringer is a type that can describe itself as a string. The fmt package (and many others) look for this interface to print values.

package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func (p Person) String() string {
	return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
	a := Person{"Arthur Dent", 42}
	z := Person{"Zaphod Beeblebrox", 9001}
	fmt.Println(a, z)
}

Output:

Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)

Errors

Go programs express error state with error values.

The error type is a built-in interface similar to fmt.Stringer:

type error interface { Error() string } (As with fmt.Stringer, the fmt package looks for the error interface when printing values.)

Functions often return an error value, and calling code should handle errors by testing whether the error equals nil.

i, err := strconv.Atoi("42") if err != nil { fmt.Printf("couldn't convert number: %v\n", err) return } fmt.Println("Converted integer:", i) A nil error denotes success; a non-nil error denotes failure.

package main

import (
	"fmt"
	"time"
)

type MyError struct {
	When time.Time
	What string
}

func (e *MyError) Error() string {
	return fmt.Sprintf("at %v, %s",
		e.When, e.What)
}

func run() error {
	return &MyError{
		time.Now(),
		"it didn't work",
	}
}

func main() {
	if err := run(); err != nil {
		fmt.Println(err)
	}
}

Concurency

Channels

A channel in Go provides a connection between two goroutines, allowing them to communicate.

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