Learning Go Notes
Original link: Learning Go Notes
GOPATH:
GO installation, including the installation location of related libraries.
Default is /usr/local/go, can be set by yourself.
$GOPATH/src stores all go program files.
Execute go install in a directory to install the package in that directory.
go build
compiles.
go get remote
fetches packages remotely, e.g.:
go get github.com/xxx/xxx
-u can automatically resolve dependencies.
go fmt
Formats code.
go test
Reads `*_test.go` files and executes tests.
Every package has a reserved function: init
.
The main
package also has a reserved function: main
.
Structs allow anonymous fields, which is equivalent to direct embedding.
type A struct { x int }
type B struct { A ; y int }
Then you can access B.x
, B.y
, and B.A.x
.
Anonymous fields can partially implement inheritance, and their mechanism can painlessly implement multiple inheritance.
The principle is:
If a struct has an anonymous field that implements an interface, then this struct also implements that interface.
Interfaces can also inherit, just by using anonymous fields of interfaces, e.g., interface kid { sup.Interface ; foo(int) int; }
.
Basic Concepts
Importing Modules
In a module, only symbols starting with an uppercase letter can be exported (used by other modules).
Importing modules, e.g.:
import "strings" is equivalent to import strings as s
import . "strings" is equivalent to import * from strings
import s "strings" is equivalent to import strings as s
Basic Types
string bool
{|u}int{|8|16|32|64}
float{32|64}
rune is equivalent to char
Variables default to zero values.
Type conversion, e.g., `int8(i)`.
Other Types
`type` is equivalent to `typedef`.
type XXX struct
type XXX interface
type XXX []int
Variable Declaration
var x int = 25
var x := 25
var a [10]int
Assigning to `_` is equivalent to discarding the value (>/dev/null).
var t = `Hello
world` is a way to declare multi-line strings.
Batch assignment is possible:
x, y = y, x
Dynamically Allocated Variables
Basic type variables use `new`:
a := new(int) returns an `*int`
*a = 5
For slices, maps, and channels, use `make`.
Constant Declaration
const x int = 0xDEADBEEF
Auto-incrementing constant sequence:
const (
x = iota // 0
y = iota // 1
z = iota // 2
)
for
and if
statements
Similar to C, but without parentheses; curly braces are mandatory.
Go uses `for` to achieve `while` loops.
Go does not have a ternary operator; `?:` needs to be done with `if`.
goto
statement
Labels must be within the current function.
switch-case
statement
Automatically breaks unless `fallthrough` is used.
Can be used without a condition to replace `if-elseif-elseif-else`.
Labels don't necessarily have to be integers.
A `case` can have multiple comma-separated conditions.
defer
Defers a function call until the outer function finishes execution.
Parameters are evaluated immediately.
With multiple `defer` calls, they are executed in LIFO order (defer stack).
Common usage:
open file xxx
defer close file xxx
lock
defer unlock
Pointers
Go has pointers, but no pointer arithmetic.
Nil pointer is `nil`.
Structs
Similar to C structs, but `p->a` is written as `p.a`, which is equivalent to `(*p).a`.
Initialization, e.g.:
p := Point(2, 3)
p := Point(x:2, y:3)
p := new(Point)
Arrays and Slices
Array declaration, e.g.: `var a [10]int {1,2,3,4,5}`
`b := [...]int {5,6,7,8,9}`
`c := [2][2]int {{1,2}, {3,4}}`
Dynamic allocation, e.g.: `a := make([]int, 10)`
`a := make([]int, 2, 10)` // capacity is 10, length is 2
A slice is a reference to the original array, with properties:
len(slice), cap(slice)
Constructed via `slice := make([]type, len, [cap])`.
In C, an array is similar to `int(*)[3]`, while a slice is similar to `int*`.
Array/slice assignment is shallow copy; deep copy requires `copy(dst, src)`.
Maps
Declaration, e.g.: `maps := map[string]int {"one": 1, "two": 2}`
Dynamic allocation, e.g.: `maps := make(map[int]int)`
Maps can be dynamically added. Non-existent keys correspond to zero values.
Methods
Go does not have classes, but it has member functions for structs (can be called as `a.fn(b)`).
(In fact, they are ordinary functions, but with a receiver).
Definition, e.g.: `func (Type a) fnname(args)`, where `a` becomes the receiver.
If a method is defined with `(T a)` as the receiver, then `T` has that method.
When the receiver type is a pointer, `a.method()` is equivalent to `(&a).method()`.
When the receiver type is a value, `(&a).method()` is equivalent to `a.method()`.
Interfaces
Define an interface, e.g.: `type Name interface { funcs }`
A struct type implements an interface's functions by simply filling in the methods of the interface with itself as the receiver.
An empty interface is similar to Java's `Object` (C's `void*`).
`i.(type)` is used to determine the type of `i`, only in a `switch` statement:
i interface ...
switch v := i.(type)
`fmt.Stringer` defines a `String()` method; implementing it allows `fmt` to print.
Error Handling
In Go, a separate return value is typically used to indicate errors, rather than exceptions or negative error codes.
If the return value is `nil`, there is no error.
Otherwise, a type implementing the `error` interface is returned, indicating an error.
If a function returns a type implementing the `error` interface, then when calling it:
if err := fn(); err != nil {
// error handling
}
Goroutines
Features:
* Managed by Go's runtime system.
* Can be seen as lightweight threads, even lighter than threads.
* Communicate via messages (rather than sharing memory).
Starting a goroutine: A function must be specified.
Named function: `go f(x, y, z)`
Anonymous function: `go func() {
// some clumsy crap
}()` // notice the pair of parentheses here
Message Passing
Creating a channel: `ch := make(chan string)` // passes string messages
Creating a buffered channel: `ch := make(chan string, buffer_sz)` // buffered channel, similar to a mailbox
Receiving messages: `v := <- ch` or `v, closed := <-ch`
Sending messages: `ch <- v`
Sending and receiving are blocking until the other side is ready.
(Of course, if the buffer has no ready data / is not full).
Closing a channel: `close(ch)` can be detected by the receiver using `v,closed:=<-ch`.
After closing a channel, buffered data is still retained,
but no more data can be sent into it.
When a channel is a function parameter, its direction can be specified, e.g., `done chan<- bool` (send-only).
`select` statement
Used to receive messages from multiple channels.
select {
case v := <-ch1: // some clumsy crap
case v2, eof := <-ch2 // crap here
default: // if all other channels are blocked
}
Design Patterns using Goroutines
fan-in:
producer1 --\
v
fan: hide the fact that there are multiple producers --> consumer
^
producer2 --/
Pipelines:
producer --> processor --> processor --> consumer
Timing
Requires `import "time"`.
Usage:
One-time timer:
<-timer.NewTimer(time.Duration(2) * time.Second).C
Blocks for one second (a better way is `time.Sleep()`).
Can call `stop()` to stop a timer.
Periodic timer:
Particularly Important Libraries
Strings
import s "strings"
s.Contains; s.HasPrefix; s.HasSuffix; ...
Regular Expressions
import re "regexp"
matchOrNot, err := re.MatchString("a?b", "b")
matcher, err := re.Compile("^a*$")
matcher := re.MustCompile("^a*$") // guaranteed to compile
var matchOrNot bool = a.MatchString("")
var firstMatch string = a.FindString("adaaasd")
JSON
Network communication primarily uses JSON now.
jsonObj, err := json.Marshal(structOrMapOrSliceOrPODObj) // encode
Time
import "time"
time.Now() time.Date(..)
timeObj.Year, Month, Minute ...
timeObj.Before, After ...
Random Numbers
import "math/rand"
rand.Intn(100) 0 <= rand.Float64() < 1
r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
Number Parsing
import "strconv"
v, err := strconv.ParseInt("233", /*from=*/0, /*maxwidth*/=32)
Reading Files
import ("bufio" "fmt" "io" "io/ioutil" "os")
dat, err := ioutil.ReadFile("/tmp/dat")
f, err := os.Open(path) is equivalent to `fd = open(path, O_RDONLY)`
f.Read(size) ([]byte, error) No buffering, equivalent to `unistd.h:read()`
r := bufio.NewReader(f) is equivalent to `fopen(path, "r")`
r.Read(data []byte) (int, error) is equivalent to `stdio.h:fread()`
f, err := os.Create(path) is equivalent to `fd = open(path, O_WRONLY, O_CREAT|O_TRUNC)`
Standard input/output/error are:
os.Stdin os.Stdout os.Stderr
Command Line Arguments
import "os"
os.Args[1:]
Generally requires compilation before using command line arguments.
HTTP
Go HTTP Server
Basics:
Uses the `net/http` package.
Requires a handler function:
func handler(rw http.ResponseWriter, r *http.Request)
->
Implemented via `HandleFunc` wrapper:
type Handler interface {
ServeHTTP(ResponseWriter, Request)
}
Then register this handler to the router in `main`:
http.HandleFunc("/", handler)
Then start the service in `main`:
err := http.ListenAndServe(":8080", nil)
// nil: uses the default ServeMux. The second parameter is also a Handler.
Connections
Each connection is served by an independent goroutine, ensuring concurrency.
Form Handling
The simplest way to display a page with a form is:
t, _ := template.ParseFiles("page1.template")
t.Execute(w, nil)
// template automatically prevents injection attacks.
In the page handler function that processes the form, to identify the form, you first need to:
r.ParseForm()
This parses both GET and POST forms.
Accessing form values can be done using:
r.Form["key"]
// r.Form type is map[string][]string
// because HTML allows multiple controls with the same name.
Form validation is usually done using regular expressions.
Escaping can use the following functions:
func HTMLEscape(w io.Writer, b []byte)
func HTMLEscapeString(s String) string
To prevent multiple form submissions, you can add a random hidden field to the form (only for non-malicious users).
Web Templates
https://golang.org/pkg/text/template/
Uses `html/template`. As above,
t.Execute(w, obj)
`obj` is a struct used to render the template. In the template file, use:
{{ .Field }}
to access `obj.Field`. Must be an exported field.
{{ if .Flag }} ... {{ else }} ... {{ end }}
{{ range .Arr }} {{ ... }} {{ end }}
Uploading Files
To upload files, the form's encoding type must be set:
<form enctype="multipart/form-data" action="/upload" method="post"> ...
And add a file input field:
<input type="file" name="uploadedfile"/>
In the page that handles file uploads, you first need to parse the form:
r.ParseMultipartForm(1 << 20)
mimeFile, mimeHeader, err := r.FormFile("uploadedfile");
if err != nil { ... }
defer mimeFile.Close()
Here, `1<<20` means the memory buffer is 1MB; parts of the file exceeding 1MB are written to disk.
Proper settings can reduce disk burden.
You can access the filename using `mimeHeader.Filename`;
`mimeFile` implements `Reader`, `ReaderAt`, `Seeker`, `Closer`, and can be used directly:
nread, err := mimeFile.Read(size)
You can also use:
ntrans, err := io.Copy(dstFile, mimeFile)
But they cannot be mixed: `io.Copy` does not rewind.
RPC
GO RPC
There are different frameworks; here we use `net/rpc`.
RPC basically means a server registers an object, exposing it as a service to the outside.
The outside can call methods of this object.
Exposed services must be:
func (t *T) MethodName(argType T1, replyType *T2) error;
Where `argType`, `replyType` must be serializable by `gob` (marshal).
Parameters are passed in `T1`, successful execution returns value in `T2`, otherwise only `error` is returned.