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 havemember
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
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 packagego run file.go
- executes a filego install
- places the executables and the packages in the bin/ and pkg/ foldersgo 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)
}
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)
}
}
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.
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
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))
}
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")
}
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())
}
}
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())
}
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)
}
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()
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 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)
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
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)
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)
}
}
A channel in Go provides a connection between two goroutines, allowing them to communicate.