Go Syntax Overview and Practice List (V0.5)

Go CheatSheet is an inventory of grammar and skills in the Go learning/practice process. It belongs to the Awesome CheatSheet series, which aims to improve learning speed and r&d efficiency. It can be used as a quick guide or a lightweight introductory learning material. This article refers to many excellent articles and code demonstration, unified declaration in Go Links; If you want to learn more about something, continue reading Go Development: Syntax Basics and engineering practices, or Go to coding-Snippets/Go to see code implementations that use Go to address common data structures and algorithms, design patterns, and business functions.

Environment configuration and syntax basics

You can download the Go SDK installation package here, or install it using a package manager such as BREW. The go command relies on the $GOPATH environment variable for code organization, and ln can also be used for directory mapping in multi-project scenarios to facilitate project management. GOPATH allows you to set up multiple directories, each containing three subdirectories: SRC for source code, PKG for compiled files, and bin for compiled executable files.

After the environment is configured, you can use Go Get to get dependencies, go Run to run the program, and go Build to compile the project to generate an executable that matches the package name (folder name). Golang 1.8 supports deP dependency management tools. For empty projects, use deP init to initialize dependency configuration, which generates gopkg.toml gopkg.lock vendor/.

We can add dependencies using dep ensure -add github.com/pkg/errors, which, when run, will add the following locks to the TOML file:

[[constraint]] name = "github.com/pkg/errors" version = "0"Copy the code

The simple Go Hello World code looks like this:

package main
import "fmt"
func main(a) {
    fmt.Println("hello world")}Copy the code

You can also use Beego to implement a simple HTTP server:

package main
import "github.com/astaxie/beego"
func main(a) {
	beego.Run()
}
Copy the code

Instead of introducing relative paths, Go defines modules in folders, such as creating a new folder named Math and using Package Math to declare the module to which the functions in that file belong.

import (
        mongo "mywebapp/libs/mongodb/db"// Rename the imported module _"mywebapp/libs/mysql/db"// Use a blank underline to indicate that only its initialization function is called.Copy the code

External reference of this module requires the use of working interval or vendor relative directory, and its directory index is as follows:

cannot find package "sub/math" in any of:
    ${PROJECTROOT}/vendor/sub/math (vendor tree)
    /usr/local/ Cellar/go / 1.10 / libexec/SRC/sub/math (the from$GOROOT)
    ${GOPATH}/src/sub/math (from $GOPATH)
Copy the code

Go requires a package declaration at the beginning of each source file, and the executable file is placed in the main package by default. Functions in each package that default to uppercase are exported functions visible to other packages, while lowercase functions default to private functions that are not visible externally.

Expression and control flow

Variable declaration and assignment

As a strongly typed static language, Go allows us to identify data types after variables and also gives us automatic type derivation.

// Declare three variables, all of type bool
var c, python, java bool

// Declare different types of variables and assign values
var i bool, j int = true.2

// Complex variable declarations
var (
	ToBe   bool       = false
	MaxInt uint64     = 1<<64 - 1
	z      complex128 = cmplx.Sqrt(- 5 + 12i))// Short declaration of variables
c, python, java := true.false."no!"

// Declare constants
const constant = "This is a constant"
Copy the code

In Go, if we need to compare the similarity of two complex objects, we can use the reflect.deepequal method:

m1 := map[string]int{
    "a":1."b":2,
}
m2 := map[string]int{
    "a":1."b":2,
}
fmt.Println(reflect.DeepEqual(m1, m2))
Copy the code

conditional

Go provides enhanced if statements for conditional judgment:

// Basic form
if x > 0 {
	return x
} else {
	return -x
}

// Add a custom statement before the condition judgment
if a := b + c; a < 42 {
	return a
} else {
	return a - 42
}

// Common type judgment
var val interface{}
val = "foo"
if str, ok := val.(string); ok {
	fmt.Println(str)
}
Copy the code

Go also supports the use of Switch statements:

// Base format
switch operatingSystem {
case "darwin":
	fmt.Println("Mac OS Hipster")
	// Default break, no explicit declaration required
case "linux":
	fmt.Println("Linux Geek")
default:
	// Windows, BSD, ...
	fmt.Println("Other")}// Similar to if, you can add custom statements before conditions
switch os := runtime.GOOS; os {
case "darwin":... }// Use the switch statement to determine the type:
switch v := anything.(type) {
  case string:
    fmt.Println(v)
  case int32.int64:...default:
    fmt.Println("unknown")}Copy the code

Comparison is also supported in Switch:

number := 42
switch {
	case number < 42:
		fmt.Println("Smaller")
	case number == 42:
		fmt.Println("Equal")
	case number > 42:
		fmt.Println("Greater")}Copy the code

Or do multi-condition matching:

var char byte = '? '
switch char {
	case ' '.'? '.'&'.'='.The '#'.'+'.The '%':
		fmt.Println("Should escape")}Copy the code

cycle

Go supports loops using for statements, with no while or until:

for i := 1; i < 10; i++ {
}

// while - loop
for ; i < 10; {}// The semicolon can be ignored in the single condition
for i < 10{}// ~ while (true)
for{}Copy the code

We can also iterate over Arrays and Slices using the range function:

// loop over an array/a slice
for i, e := range a {
    // I represents the subscript, e represents the element
}

// Only elements are required
for _, e := range a {
    // e is the element
}

// Or just subscripts
for i := range a {
}

// Execute on time
for range time.Tick(time.Second) {
    // do it once a sec
}
Copy the code

Function: the Function

Define, parameters and return values

// Simple function definition
func functionName(a) {}

// Function definition with parameters
func functionName(param1 string, param2 int) {}

// Function definitions for multiple arguments of the same type
func functionName(param1, param2 int) {}

// Function expression definition
add := func(a, b int) int {
	return a + b
}
Copy the code

Go supports the last argument to the function using… Set to indeterminate, that is, one or more parameter values can be passed:

func adder(args ...int) int {
	total := 0
	for _, v := range args { // Iterates over the arguments whatever the number.
		total += v
	}
	return total
}

adder(1.2.3) / / 6
adder(9.9) / / 18

nums := []int{10.20.30}
adder(nums...) / / 60
Copy the code

We can also pass in a Function Stub as a Function argument to implement the Function callback Function:

func Filter(s []int, fn func(int) bool) []int {
    var p []int // == nil
    for _, v := range s {
        if fn(v) {
            p = append(p, v)
        }
    }
    return p
}
Copy the code

Although Go is not a functional language, it is possible to use Go to implement Currying functions:

func add(x, y int) int {
    return x+ y
}

func adder(x int) (func(int) int) {
    return func(y int) int {
        return add(x, y)
    }
}

func main(a) {
	add3 := adder(3)
	fmt.Println(add3(4))    / / 7
}
Copy the code

Go supports multiple return values:

Return a single value
func functionName(a) int {
    return 42
}

// Return multiple values
func returnMulti(a) (int.string) {
    return 42."foobar"
}
var x, str = returnMulti()

// Name returns multiple values
func returnMulti2(a) (n int, s string) {
    n = 42
    s = "foobar"
    // n and s will be returned
    return
}
var x, str = returnMulti2()
Copy the code

Closure: a Closure

Go also supports lexical scope and variable retention, so we can use closures to access variables outside the function definition:

func scope(a) func(a) int{
    outer_var := 2
    foo := func(a) int { return outer_var}
    return foo
}
Copy the code

Enclosing variables cannot be modified directly in closures. Instead, new variable values are automatically redefined:

func outer(a) (func(a) int.int) {
    outer_var := 2
    inner := func(a) int {
        outer_var += 99
        return outer_var // => 101 (but outer_var is a newly redefined
    }
    return inner, outer_var // => 101, 2 (outer_var is still 2, not mutated by inner!)
}
Copy the code

Function performs

The defer keyword is provided in Go, which allows the execution of a statement to be deferred until the function returns a statement:

func read(...). (...). {
  f, err := os.Open(file)
  ...
  defer f.Close()
  ...
  return.// f will be closed
Copy the code

Exception handling

There are no try-catch and other exception handling keywords in Go. For functions that may return an exception, we simply add an additional value of Error to the function return value:

type error interface {
    Error() string
}
Copy the code

A function call that may return an exception looks like this:

import (
    "fmt"
    "errors"
)

func main(a) {
    result, err:= Divide(2.0)

    iferr ! =nil {
            fmt.Println(err)
    }else {
            fmt.Println(result)
    }
}

func Divide(value1 int,value2 int)(int, error) {
    if(value2 == 0) {return 0, errors.New("value2 mustn't be zero")}return value1/value2  , nil
}
Copy the code

Go also provides us with a panic function, which is commonly used to throw abnormal results when the expected result is not obtained. For example, when we get an exception returned by a function and do not know how to handle it or do not need to handle it, we can directly interrupt the current operation through panic function, print error information, Goroutine trace information, and return a non-zero status code:

_, err := os.Create("/tmp/file")
iferr ! =nil {
	panic(err)
}
Copy the code

Data types and structures

Type binding and initialization

The type keyword in Go can rename a type:

// IntSlice is not equivalent to []int, but can be converted using type conversions
type IntSlice []int
a := IntSlice{1.2}
Copy the code

We can use T(v) or obj.(T) for type conversions. Obj.(T) only works with interface{} :

t := obj.(T) // if obj is not T, error
t, ok := obj.(T) // if obj is not T, ok = false

// Type conversion and judgment
str, ok := val.(string);
Copy the code

Basic data types

interface {} // ~ java Object
bool // true/false
string
int8  int16  int32  int64
int // =int32 on 32-bit, =int64 if 64-bit OS
uint8 uint16 uint32 uint64 uintptr
uint
byte // alias for uint8
rune // alias for int32, represents a Unicode code point
float32 float64
Copy the code

string

// Multi-line string declaration
hellomsg := '"Hello" in Chinese is Hello ('Ni Hao') "Hello" in Hindi is supported
Copy the code

Format string:

fmt.Println((п ривет, ᎣᏏᏲ)) // basic print, plus newline
p := struct { X, Y int} {17.2 }
fmt.Println( "My point:", p, "x coord=", p.X ) // print structs, ints, etc
s := fmt.Sprintln( "My point:", p, "x coord=", p.X ) // print to string variable

fmt.Printf("%d hex:%x bin:%b fp:%f sci:%e".17.17.17.17.0.17.0) // c-ish format
s2 := fmt.Sprintf( "%d %f".17.17.0 ) // formatted print to string variable
Copy the code

Sequence types

Both Array and Slice can be used to represent sequential data and are related to each other.

Array

Array is used to represent a fixed length sequence object of the same type, which can be created in the following form:

[N]Type [N]Type{value1, value2, ... , valueN}// The compiler automatically calculates the number[...]. Type{value1, value2, ... , valueN}Copy the code

Its specific use mode is as follows:

// Array declaration
var a [10]int

/ / assignment
a[3] = 42

/ / read
i := a[3]

// Declare and initialize
var a = [2]int{1.2}
a := [2]int{1.2}
a := [...]int{1.2}
Copy the code

Go has built-in len and CAP functions to get the size and capacity of an array:

var arr = [3]int{1.2.3}
arr := [...]int{1.2.3}

len(arr) / / 3
cap(arr) / / 3
Copy the code

Unlike Pointers in C/C++ or Object references in Java, arrays in Go are simply values. This means that when an array copy is made, or when a parameter is passed in a function call, all copies of the elements are copied, not just Pointers or references. Obviously, such replication would be expensive.

Slice

Slice gives us a more flexible and lightweight way to manipulate sequence types. Slice can be created as follows:

// create using built-in functions
make([]Type, length, capacity)
make([]Type, length)

// Declare an indeterminate length array[]Type{} []Type{value1, value2, ... , valueN}// Slice the existing array
array[:]
array[:2]
array[2:]
array[2:3]
Copy the code

Unlike Array, Slice can be considered a more flexible Reference Type. It does not actually hold an Array value, but rather a structure containing the PTR, Len, and CAP attributes. In other words, Slice can be viewed as a description of a segment in an array, including a pointer to the array, the length of the segment, and the maximum potential length of the segment, as shown below:

// Create Slice with len = 5 and cap = 5
s := make([]byte.5)

// Slice twice with len = 2 and cap = 3
s = s[2:4]

// Restore the length of Slice
s = s[:cap(s)]
Copy the code

Note that slicing does not actually copy the Slice median, but creates a new pointer to the original array, ensuring that slicing is as efficient as manipulating array subscripts. However, if we modify the value in Slice, it actually modifies the value in the underlying array and is reflected in the original array:

d := []byte{'r'.'o'.'a'.'d'}
e := d[2:]
// e == []byte{'a', 'd'}
e[1] = 'm'
// e == []byte{'a', 'm'}
// d == []byte{'r', 'o', 'a', 'm'}
Copy the code

Go provides a built-in append function to dynamically add data to Slice, which returns a new Slice object containing the original Slice median as well as the new value. If the existing Slice does not have enough capacity to hold the new sequence, new memory is automatically allocated:

// len=0 cap=0 []
var s []int

// len=1 cap=2 [0]
s = append(s, 0)

// len=2 cap=2 [0 1]
s = append(s, 1)

// len=5 cap=8 [0 1 2 3 4]
s = append(s, 2.3.4)

/ / use... To automatically expand the array
a := []string{"John"."Paul"}
b := []string{"George"."Ringo"."Pete"}
a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])"
// a == []string{"John", "Paul", "George", "Ringo", "Pete"}
Copy the code

Slice copies can also be made using the built-in copy function, which automatically uses the minimum number of elements to copy slices of different lengths. Copy also automatically handles Slice copies between arrays that use the same underlying array to avoid wasting extra space.

func copy(dst, src []T) int// Request a larger space capacityt: =make([]byte.len(s).(cap(s)+ 1) * 2)copy(t, s)
s = t
Copy the code

Mapping type

var m map[string]int
m = make(map[string]int)
m["key"] = 42

// Delete a key
delete(m, "key")

// Tests whether the value corresponding to the key exists
elem, has_value := m["key"]

// map literal
var m = map[string]Vertex{
    "Bell Labs": {40.68433.74.39967},
    "Google":    {37.42202.122.08408}},Copy the code

Struct & Interface: Struct and Interface

Struct: structure

There is no concept of classes in Go, only structures, which can be regarded as collections of attributes and can define methods for them.

// Declare the structure
type Vertex struct {
    // Struct attributes follow the same uppercase export, lowercase private principle
    X, Y int
    z bool
}

Implicit structs can also be declared
point := struct {
	X, Y int} {1.2}

// Create a struct instance
var v = Vertex{1.2}

// Read or set properties
v.X = 4;

// Display the declaration key
var v = Vertex{X: 1, Y: 2}

// Declare an array
var v = []Vertex{{1.2}, {5.2}, {5.5}}
Copy the code

The method declaration is also very succinct, just declare the structure pointer between the func keyword and the function name, and the structure will be copied between different methods:

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

// Call method
v.Abs()
Copy the code

For methods that modify the current structure object, we pass Pointers:

func (v *Vertex) add(n float64) {
    v.X += n
    v.Y += n
}
Copy the code
var p *Person = new(Person) // pointer of type Person
Copy the code

Pointer to the Pointer:

// p is the Vertex type
p := Vertex{1.2}  

// q is a pointer to Vertex
q := &p

// r is also a pointer to Vertex objects
r := &Vertex{1.2}

// The pointer to the Vertex structure object is of type *Vertex
var s *Vertex = new(Vertex)
Copy the code

Interface: the Interface

Go allows us to achieve polymorphism in a way that defines interfaces:

// Interface declaration
type Awesomizer interface {
    Awesomize() string
}

// Structs do not need to explicitly implement interfaces
type Foo struct {}

// Instead, the interface is implemented by implementing all the methods specified by the interface
func (foo Foo) Awesomize(a) string {
    return "Awesome!"
}
Copy the code
type Shape interface {
   area() float64
}

func getArea(shape Shape) float64 {
   return shape.area()
}

type Circle struct {
   x,y,radius float64
}

type Rectangle struct {
   width, height float64
}

func(circle Circle) area(a) float64 {
   return math.Pi * circle.radius * circle.radius
}

func(rect Rectangle) area(a) float64 {
   return rect.width * rect.height
}

func main(a) {
   circle := Circle{x:0,y:0,radius:5}
   rectangle := Rectangle {width:10, height:5}

   fmt.Printf("Circle area: %f\n",getArea(circle))
   fmt.Printf("Rectangle area: %f\n",getArea(rectangle))
}
/ / Circle area: 78.539816
/ / a Rectangle area: 50.000000
Copy the code

The usual idea is to define the interface first, then the implementation, and finally the methods used:

package animals

type Animal interface {
	Speaks() string
}

// implementation of Animal
type Dog struct{}
func (a Dog) Speaks(a) string { return "woof" }

/** directly references **/ where needed

package circus

import "animals"

func Perform(a animal.Animal) { return a.Speaks() }
Copy the code

Go also provides us with another interface implementation scheme. We can not define the interface in the specific implementation, but in the place where the interface needs to be used. The mode is as follows:

func funcName(a INTERFACETYPE) CONCRETETYPE
Copy the code

Define the interface:

package animals

type Dog struct{}
func (a Dog) Speaks(a) string { return "woof" }

/** Define the interface **/ where the implementation is needed
package circus

type Speaker interface {
	Speaks() string
}

func Perform(a Speaker) { return a.Speaks() }
Copy the code

Embedding

There is no such concept of subclass inheritance in Go language, but realizes the combination of classes or interfaces by Embedding.

// The implementation of ReadWriter must satisfy both Reader and Writer
type ReadWriter interface {
    Reader
    Writer
}

// Server exposes all Logger struct methods
type Server struct {
    Host string
    Port int
    *log.Logger
}

// The initialization mode is not affected
server := &Server{"localhost".80, log.New(...) }// You can call the methods of the embedded structure directly, equivalent to server.logger.log (...).
server.Log(...)

// The noun of the embedded structure is the type name
var logger *log.Logger = server.Logger
Copy the code

Concurrent programming

Goroutines

Goroutines are lightweight threads. See introduction to Concurrent Programming for a discussion of processes, threads, and coroutines. Go provides us with a very handy Goroutines syntax:

// A normal function
func doStuff(s string){}func main(a) {
    // Create a Goroutine using a named function
    go doStuff("foobar")

    // Create a Goroutine using an anonymous inner function
    go func (x int) {
        // function body goes here} (42)}Copy the code

Channels

A Channel is a typed pipe that can be used to pass messages between different Goroutines. The basic operations are as follows:

// Create channel of type int
ch := make(chan int)

// Send a value to the channel
ch <- 42

// Get the value from the channel
v := <-ch

// Read, and determine if it is closed
v, ok := <-ch

// Read the channel until it closes
for i := range ch {
    fmt.Println(i)
}
Copy the code

For example, we could wait for a message from Goroutine in the main thread and print:

// Create channel
messages := make(chan string)

/ / execution Goroutine
go func(a) { messages <- "ping"} ()// block and wait for messages
msg := <-messages

// Use channels to compute concurrently, and block waiting for results
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
Copy the code

Non-buffered Channels are blocked Channels. The reader continues to block when there is no value, and the writer blocks when there is no read. We can create a Buffered Channel whose reader will not block until the Channel is full:

ch := make(chan int.100)

// The sender can also voluntarily close the channel
close(ch)
Copy the code

Channel can also be used as a function argument, and we can explicitly declare whether it is used to send or receive information, thus increasing the type safety of the program:

// The ping function is used to send information
func ping(pings chan<- string, msg string) {
    pings <- msg
}

// The pong function is used to receive information from one channel and send it to another channel
func pong(pings <-chan string, pongs chan<- string) {
    msg := <-pings
    pongs <- msg
}

func main(a) {
    pings := make(chan string.1)
    pongs := make(chan string.1)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}
Copy the code

synchronous

Synchronization is a common requirement in concurrent programming. Here we can use the blocking feature of channels to implement synchronization between Goroutines:

func worker(done chan bool) {
    time.Sleep(time.Second)
    done <- true
}

func main(a) {
    done := make(chan bool.1)
    go worker(done)

	// block until a message is received
    <-done
}
Copy the code

Go also provides us with the SELECT keyword to wait for execution results from multiple channels:

// Create two channels
c1 := make(chan string)
c2 := make(chan string)

// Each channel outputs different values with different delay
go func(a) {
	time.Sleep(1 * time.Second)
	c1 <- "one"} ()go func(a) {
	time.Sleep(2 * time.Second)
	c2 <- "two"} ()// Use select to wait for the results of both channels
for i := 0; i < 2; i++ {
	select {
	case msg1 := <-c1:
		fmt.Println("received", msg1)
	case msg2 := <-c2:
		fmt.Println("received", msg2)
	}
}
Copy the code

Web programming

HTTP Server

package main

import (
    "fmt"
    "net/http"
)

// define a type for the response
type Hello struct{}

// let that type implement the ServeHTTP method (defined in interface http.Handler)
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello!")}func main(a) {
    var h Hello
    http.ListenAndServe("localhost:4000", h)
}

// Here's the method signature of http.ServeHTTP:
// type Handler interface {
// ServeHTTP(w http.ResponseWriter, r *http.Request)
// }
Copy the code

Beego

Using bee command line tools recommended by Beego, we can quickly create Beego projects. The directory organization is as follows:

├── ├─ ├─ controllers │ ├─default.go├ ─ ─ the main.go├── Bass Exercises ─ exercises for more than two years.go├ ─ ─ the static │ ├ ─ ─ CSS │ ├ ─ ─ img │ └ ─ ─ js ├ ─ ─ tests │ └ ─ ─ default_test.go└ ─ ─ views └ ─ ─ index. The TPLCopy the code

In the main.go file, we can launch the Beego instance and invoke the initial configuration file for the route:

package main

import(_"quickstart/routers"
        "github.com/astaxie/beego"
)

func main(a) {
        beego.Run()
}
Copy the code

In the route initialization function, we will declare the mapping between each route and the controller:

package routers

import (
        "quickstart/controllers"
        "github.com/astaxie/beego"
)

func init(a) {
        beego.Router("/", &controllers.MainController{})
}
Copy the code

Static resource mappings in Beego projects can also be manually specified:

beego.SetStaticPath("/down1"."download1")
beego.SetStaticPath("/down2"."download2")
Copy the code

In a specific controller, you can set the returned data, or the associated template name:

package controllers

import (
        "github.com/astaxie/beego"
)

type MainController struct {
        beego.Controller
}

func (this *MainController) Get(a) {
        this.Data["Website"] = "beego.me"
        this.Data["Email"] = "[email protected]"
        this.TplNames = "index.tpl" // version 1.6 use this.TplName = "index.tpl"
}
Copy the code

DevPractics: Development practices

File to read and write

import (
    "io/ioutil")... datFile1, errFile1 := ioutil.ReadFile("file1")
iferrFile1 ! =nil {
	panic(errFile1)
}
...
Copy the code

test

VSCode automatically generates basic test cases for functions and provides easy use case execution and debugging.

/** swap function */
func swap(x *int, y *int) {
	x, y = y, x
}

/** Automatically generated test function */
func Test_swap(t *testing.T) {
	type args struct {
		x *int
		y *int
	}
	tests := []struct {
		name string
		args args
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			swap(tt.args.x, tt.args.y)
		})
	}
}
Copy the code