go programming language (golang)
TRANSCRIPT
Contents
• Introduction
• Why Go?
• Important note
• Basic commands
• Basic Syntax
• net/http package
Introduction
Go is initially developed at Google in 2007.
Go was announced in November 2009 and is used in some of the Google’s production systems.
Go is an open source programming language.
Go 1 was released in March 2012
Currently, Go 1.8 released in 16 February 2017
Go is a general-purpose language.
Go is statically typed, compiled language.
Why Go?
Go is an open-source but backed up by a large corporation.
Automatic memory management (garbage collection).
Strong focus on support for concurrency.
Fast compilation and execution
Statically type, but feels like dynamically typed
Good cross-platform support
Go compiles to native machine code
Rapid development and growing community
Used by big companies (Google, Dropbox, Docker, Cloudflare, Soundcloud)
Important note
No type inheritance
No method or operator overloading
No support for pointer arithmetic
No support for Assertions
No Exceptions - instead use an error return type
No Generics support
Too much Boilerplate (codes that have to be included in many places with little or no alteration)
Bad dependency management
Go uses URL based dependency imports
go get http://github.com/somename/somelibrary
import github.com/somename/somelibrary
Basic commandsgo is a tool for managing Go source code.
Usage:
go command [arguments]
The commands are:
build compile packages and dependencies
clean remove object files
doc show documentation for package or symbol
env print Go environment information
get download and install packages and dependencies
install compile and install packages and dependencies
run compile and run Go program
test test packages
version print Go version
Use "go help [command]" for more information about a command.
Go keywords
default
defer
else
fallthrough
for
Go has only 25 keywords, even less than English alphabet!
break
case
chan
const
continue
func
go
goto
if
import
interface
map
package
range
return
select
struct
switch
type
var
Basic types
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32
float32 float64
complex64 complex128
Packages
Every Go program is made up of package.
Program start running in package main.
Name is exported if it begins with a capital letter.
package main
import (
“fmt”
“math”
)
func main () {
fmt.Println(math.pi) //error
fmt.Println(math.Pi)
}
Functions// type comes after the variable name
func add(x int, y int) int {
return x + y
}
// when arguments share the same type, type can be omitted but the last
func add(x, y int) int {
return x + y
}
func swap(x, y string) (string, string) {
return y, x // return multiple values
}
func hello(x, y string) (a, b string) {
a = x
b = y
return // name return value or naked return
}
Variables
// variables with initializers
var i, j int = 1, 2
var c, golang, java = false, true, “no!”
var c, golang, java bool
func main() {
var i int
}
Use var statement declare a list of variables.
Inside a functions, the := short assignment can be use in place of var
func main() {
c, golang, java := false, true, “no!”
}
Zero values
var i int
var f float64
var b bool
var s string
fmt.Printf(“%v %v %v %q\n”, i, f, b, s)
// output: 0 0 false “”
Variables declared without initial value are given their zero values.
0 for numeric types
false for boolean type
“” for string type
Type conversion
// Go assignment between items of different type requires an explicit conversion
var i int = 42
var f float64 = float64(i)
var u unit = unit(f)
Constants
// Constants are declared like variables, but with the const keyword
const Pi = 3.14
For
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
// note: no () surrounding the three components; {} always required
Go has only one looping construct, the for loop.
For is Go’s while
sum := 1
for sum < 1000 {
sum += sum
}
If else
if x := math.Sqrt(100); x < 10 {
fmt.Printf(“Square root of 100 is %v\n”, x)
} else {
fmt.Printf(“Square root of 100 is %v\n”, x)
}
// note: no () surrounding the condition; {} always required
If can start with a short statement to execute before condition.
Switch
package main
import (
"fmt”
"runtime”
)
func main() {
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println(" Go runs on OS X.”)
case "linux":
fmt.Println(" Go runs on Linux.”)
default:
fmt.Printf(" Go runs on %s.", os)
}
}
Switch can start with a short statement to execute before condition.
Defer
func main() {
fmt.Println("counting down”)
defer fmt.Print(”\ndone”)
for i := 0; i < 10; i++ {
defer fmt.Print(i, “ ”)
}
}
// output:
counting down
9 8 7 6 5 4 3 2 1 0
done
Defers the execution of a function until the surrounding function returns
Pointer
i := 21
p := &i // store address of i in pointer variable p
fmt.Printf(“Address stored in p variable: %p\n”, p) // print address
fmt.Printf(“Value of i is: %v\n”, *p) // read i through the pointer
*p = 77 // set i through the pointer
fmt.Printf(“New value of i is: %v\n”, i) // see the new value of i
Pointer is a variable whose value is the address of another variable.
Ampersand (&) operator denotes an address in memory.
Asterisk (*) operator denotes the pointer's underlying value.
Output:
Address stored in p variable: 0x1040e0f8
Value of i is: 21
New value of i is: 77
Struct
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
p := &v
p.X = 1e9
fmt.Println(v)
}
// output: {1000000000 2}
Struct is a collection of fields
Struct fields are accessed using a dot
Struct fields can be accessed through a struct pointer
Arrays & Slicesvar a [10]int
Array has a fixed size.
Slice is a dynamically-size, flexible view of an array; var a []int
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4]
fmt.Println(s)
// output: [3 5 7]
Slice is like reference to array; it does not store any data.
names := [4]string{“Jonh”, “Paul”, “George”, “Ringo”}
fmt.Println(names) // [John Paul George Ringo]
a := names[1:3]
fmt.Println(a) // [Paul George]
a[0] = "XXX”
fmt.Println(a) // [XXX George]
fmt.Println(names) // [John XXX George Ringo]
Make function
func printSlice(s string, x []int) {
fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x)
}
func main() {
a := make([]int, 5) // len(a) = 5; length of a is 5
printSlice("a", a) // output: a len=5 cap=5 [0 0 0 0 0]
b := make([]int, 0, 5) // len(b) = 0, cap(b) = 5
printSlice("b", b) // output: b len=0 cap=5 []
}
Slices can be created with the built-in make function; this is how you create dynamically-sized array.
Append function
Go provides a built-in append function
func main() {
var s []int
printSlice(s) // output: len=0 cap=0 []
s = append(s, 0) // append works on nil slices.
printSlice(s) // output: len=1 cap=2 [0]
s = append(s, 1) // The slice grows as needed.
printSlice(s) // output: len=2 cap=2 [0 1]
s = append(s, 2, 3, 4) // add more than one element at a time
printSlice(s) // output: len=5 cap=8 [0 1 2 3 4]
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
Range
Range form the for loop iterates over a slice or map
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
fmt.Printf("2^%d = %d\n", i, v)
}
// output:
2^0 = 1
2^1 = 2
2^2 = 4
2^3 = 8
2^4 = 16
2^5 = 32
2^6 = 64
2^7 = 128
Mapmake function returns a map of the given type, initialized and ready to use
m := make(map[string]int) // initialized map
m["Answer"] = 42 // insert element
fmt.Println("The value:", m["Answer"]) // output: The value: 42
m["Answer"] = 48 // update element
fmt.Println("The value:", m["Answer"]) //output: The value: 48
delete(m, "Answer”) // delete element
fmt.Println("The value:", m["Answer"]) //output: The value: 0
v, ok := m["Answer”] // If key is in map, ok is true
fmt.Println("The value:", v, "Present?", ok) // If not, ok is false
// output: The value: 0 Present? false
Function closuresClosure is a function value that references variable from outside its body
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos := adder()
for i := 0; i<5; i++ {
fmt.Print(pos(i), “ ”)
}
}
// output: 0 1 3 6 10
Methods
A method is a function with a special receiver argument.
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
f := MyFloat(-21)
fmt.Println(f.Abs())
}
// output: 21
Receiver type must be defined in the same package as the method.
Pointer receiversMethods with pointer receivers can modify the value to which the receiver points
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}
fmt.Println(v.Abs()) // output: 5
v.Scale(10)
fmt.Println(v.Abs()) // output: 50
}
Interface
type I interface {
M()
}
type T struct {
S string
}
func (t T) M() { // This method means type T implements the interface I
fmt.Println(t.S)
}
func main() {
var i I = T{"hello”}
i.M() // output: hello
}
Interface type is defined as a set of method signatures.
A type implements an interface by implementing its methods.
There is no explicit declaration of intent, no "implements" keyword.
Empty interface
func main() {
var i interface{}
describe(i) // output: (<nil>, <nil>)
i = 42
describe(i) // output: (42, int)
i = "hello"
describe(i) // output: (hello, string)
}
func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}
Interface type that specifies zero methods is known as the empty interface
An empty interface may hold values of any type.
Ex: fmt.Print takes any number of arguments of type interface{}
Stringer
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}
fmt.Println(a) // output: Arthur Dent (42 years)
}
Stringer is defined by the fmt package
type Stringer interface {
String() string
}
Stringer is a type that can describe itself as a string
fmt package (and many others) look for this interface to print values
Error
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) // at 2017-03-08 23:00:00 +0700 ICT, it didn't work
}
}
The error type is a built-in interface similar to fmt.Stringer
type error interface {
Error() string
}
ReaderThe io package specifies the io.Reader interface, which represents the read end of a stream of data.
func main() {
r := strings.NewReader("Hello, Reader!”)
b := make([]byte, 8)
for {
n, err := r.Read(b)
if err == io.EOF {
break
}
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
}
}
func (T) Read(b []byte) (n int, err error)
Goroutine
Goroutines run in the same address space, so access to shared memory must be synchronized.
One goroutine usually uses 4 - 5 KB of stack memory.
Therefore, it's not hard to run thousands of goroutines on a single computer.
Goroutine is a lightweight thread managed by the Go runtime.
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world”)
say("hello")
}
// output:
world
hello
hello
world
world
hello
hello
world
world
hello
Channel
Channel is one of the features that make Golang unique.
The purpose of using Channel is to transfer values in goroutines.
ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and assign value to v.
Like maps and slices, channels must be created before use:
ch := make(chan int)
Channel: exampleSum the numbers in a slice, distributing the work between two goroutines.
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c) // [7 2 8]
go sum(s[len(s)/2:], c) // [-9 4 0]
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y) //output: -5 17 12
}
Once both goroutines completed their computation, it calculates the final result.
SelectSelect 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.
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
}
}
}
// output:
tick.
tick.
tick.
tick.
tick.
BOOM!
net/http packagePackage http provides HTTP client and server implementations.
More reader : https://golang.org/pkg/net/http/
func HandleFunc : registers the handler function for the given pattern in the DefaultServeMux. Example : http.HandleFunc("/", hello)
func ListenAndServe : listens on the TCP network address and then calls Serve with handler to handle requests on incoming connections. Accepted connections are configured to enable TCP keep-alives.
Example : http.ListenAndServe(":8080", nil)
package main
import (
"fmt"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello world!")
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":8080", nil)
}
References
1. https://golang.org
2. https://tour.golang.org
3. https://en.wikipedia.org/wiki/Go_(programming_language)
4. https://www.gitbook.com/book/astaxie/build-web-application-with-golang
Sample code : https://github.com/shin7/go-rest-api