Introduction to Go
Basic Go synax is introduced in this article.
Packages, variables, and functions
1 | package main |
Inside a function, the
:=
short assignment statement can be used in place of avar
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
- Outside a function, every statement begins with a keyword
(
Naked return statements should be used only in short functions. They can harm readability in longer functions
The expression
T(v)
converts the valuev
to the typeT
. Unlike in C, in Go assignment between items of different type requires an explicit conversionWhen declaring a variable without specifying an explicit type, the variable's type is inferred from the value on the right hand side
- When the right hand side of the declaration is typed, the new variable is of that same
- When the right hand side contains an untyped numeric constant, the
new variable may be an
int
,float64
, orcomplex128
depending on the precision of the constant
Constants cannot be declared using the
:=
syntaxNumeric constants are high-precision values. An untyped constant takes the type needed by its context
Flow control statements: for, if, else, switch and defer
1 | package main |
Go cannot have declared but not used variables
The init and post statements are optional for the for loop. For is Go's "while"
Variables declared by the short statement in the if expression are only in scope until the end of
if
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
More types: structs, slices, and maps
1 | package main |
Slices are like references to arrays. Changing the elements of a slice modifies the corresponding elements of its underlying array
A slice has both a length and a capacity
- The length of a slice is the number of elements it contains
- The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice
- The length and capacity of a slice
s
can be obtained using the expressionslen(s)
andcap(s)
- When appending to a slice with enough capacity, the appended slice will be written on the original slice. When appending to a slice without enough capacity, a new slice will be created with enough capacity and the original slice will remain the same
Functions are values too. They can be passed around just like other values
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 ownsum
variable
- For example, the
Methods and Interfaces
1 | package main |
Go does not have classes. You can instead define methods on types
- A method is a function with special receiver argument
- The receiver appears in its own argument list between the
func
keyword and the method name - You can only declare a method with a receiver whose type is defined
in the same package as the method (includes the built-in types such as
int
)
You can declare methods with pointer receivers. Methods with pointer receivers can modify the value to which the receiver points
- Another reason to use pointer receiver is to avoid copying the value on each method call which can be more efficient if the receiver is a large struct
An interface type is defined as a set of method signatures. A value of interface type can hold any value that implements those method
- A type implements an interface by implementing its methods. There is no explicit declaration of intent, no "implements" keyword
- Implicit interfaces decouple the definition of an interface from its implementation, which could then appear in any package without prearrangement
Interface value
- Under the hood, interface values can be thought of as a tuple of a
value and a concrete type:
(value, type)
- An interface value holds a value of a specific underlying concrete type
- Calling a method on an interface value executes the method of the same name on its underlying type
- Under the hood, interface values can be thought of as a tuple of a
value and a concrete type:
If the concrete value inside the interface itself is nil, the method will be called with a nil receiver
- A nil interface value holds neither value nor concrete type
- Calling a method on a nil interface is a run-time error because there is no type inside the interface tuple to indicate which concrete method to call
The interface type that specifies zero methods is known as the empty interface:
interface{}
- An empty interface may hold values of any type
- Empty interfaces are used by code that handles values of unknown
type. For example,
fmt.Print
takes any number of arguments of typeinterface{}
A type assertion provides access to an interface value's underlying concrete value:
t := i.(T)
- This statement asserts that the interface value
i
holds the concrete typeT
and assigns the underlyingT
value to the variablet
- If
i
does not hold aT
, the statement will trigger a panic - To test whether an interface value holds a specific type, a type assertion can return two values: the underlying value and a boolean value that reports whether the assertion succeeded. This syntax is similar to map's
- This statement asserts that the interface value
One of the most ubiquitous interfaces is
Stringer
defined by thefmt
package1
2
3type Stringer interface {
String() string
}- A
Stringer
is a type that can describe itself as a string
- A
Go programs express error state with
error
values1
2
3type error interface {
Error() string
}- Functions often return an
error
value, and calling code should handle errors by testing whether the error equalsnil
- A nil
error
denotes success; a non-nilerror
denotes failure
- A nil
- This third party package is often used together with error: github.com/pkg/errors
- Functions often return an
The
io
package specifies theio.Reader
interface, which represents the read end of a stream of data1
func (T) Read(b []byte) (n int, err error)
Read
populates the given byte slice with data and returns the number of bytes populated and an error value. It returns anio.EOF
error when the stream ends
There is no check of whether a type implemented all of the methods of an interface
To force a check, we can do:
1
var _ Shape = (*Square)(nil)
If not all methods are implemented, the compiler shall give the following error:
1
2cannot use (*Square)(nil)(type *Square) as type Shape in assignment:
*Square does not implement Shape (missing Area method)
Concurrency
1 | package main |
A goroutine is a lightweight thread managed by the Go runtime.
go f(x)
starts a new goroutine runningf(x)
The evaluation of
f
andx
happens in the current goroutine and the execution off
happens in the new goroutineGoroutines run in the same address space, so access to shared memory must be synchronized
If we want to make sure only one goroutine can access a variable at a time to avoid conflicts, we can use mutual exclusion, i.e. mutex
- We can define a block of code be executed in mutual exclusion by
surrounding it with a call to
Lock
andUnlock
as shown above
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x,y = y, x+y
}
close(c)
}
func fib2 (c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
s := []int {7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
bc := make(chan int, 2)
bc <- 1
bc <- 2
fmt.Println(<-bc)
fmt.Println(<-bc)
fc := make(chan int, 10)
go fibonacci(cap(fc), fc)
for i := range fc {
fmt.Println(i)
}
fc2 := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fib2(c, quit)
}- We can define a block of code be executed in mutual exclusion by
surrounding it with a call to
Channels are a typed conduit through which you can send and receive values with the channel operator,
<-
1
2ch <- v // Send v to channel ch
v := <-ch // Receive from ch, and assign value to vChannels must be created before use:
ch := make(chan int)
By default, sends and receives block until the other side side ready. This allows goroutines to synchronize without explicit locks or condition variables
Channels can be buffered. Provide the buffer length as the second argument to to
make
to initialize a buffered channel:ch := make(chan int, size)
Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty
A sender can
close
a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression:v, ok := <-ch
,ok
is false if there are no more values to receive and the channel is closed- Sending on a closed channel will cause a panic
The
select
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
- A
Beego ORM
- Beego is a ORM library with GO style database management
- Installation
1
go get github.com/astaxie/beego
- Usage
1
2
3
4
5
6
7
8
9
10
11
12import (
"database/sql"
"github.com/astaxie/beego/orm"
"github.com/go-sql-driver/mysql"
)
func init() {
orm.RegisterDriver("mysql", orm.DR_MySQL)
orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
orm.RegisterModel(new(User))
orm.RunSyncdb("default", false, true)
}