• Golang语法、技巧和窍门


    在这里插入图片描述

    Golang简介

    • 命令式语言
    • 静态类型
    • 语法标记类似于C(但括号较少且没有分号),结构类似Oberon-2
    • 编译为本机代码(没有JVM)
    • 没有类,但有带有方法的结构
    • 接口
    • 没有实现继承。不过有type嵌入
    • 函数是一等公民
    • 函数可以返回多个值
    • 支持闭包
    • 指针,但没有指针算术
    • 内置并发原语:Goroutines和Channels

    基本语法

    你好,世界

    文件 hello.go

    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("Hello Go")
    }
    $ go run hello.go
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    运算符

    算术运算符
    运算符描述
    +加法
    -减法
    *乘法
    /除法
    %取余
    &位与
    ``
    ^位异或
    &^位清除(非)
    <<左移
    >>右移
    比较运算符
    运算符描述
    ==等于
    !=不等于
    <小于
    <=小于等于
    >大于
    >=大于等于
    逻辑运算符
    运算符描述
    &&逻辑与
    `
    !逻辑非
    其他
    运算符描述
    &取地址 / 创建指针
    *解引用指针
    <-发送 / 接收操作符(见下面的‘通道’部分)

    声明

    类型在标识符之后!

    var foo int // declaration without initialization
    var foo int = 42 // declaration with initialization
    var foo, bar int = 42, 1302 // declare and init multiple vars at once
    var foo = 42 // type omitted, will be inferred
    foo := 42 // shorthand, only in func bodies, omit var keyword, type is always implicit
    const constant = "This is a constant"
    
    // iota can be used for incrementing numbers, starting from 0
    const (
        _ = iota
        a
        b
        c = 1 << iota
        d
    )
        fmt.Println(a, b) // 1 2 (0 is skipped)
        fmt.Println(c, d) // 8 16 (2^3, 2^4)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    函数

    // a simple function
    func functionName() {}
    
    // function with parameters (again, types go after identifiers)
    func functionName(param1 string, param2 int) {}
    
    // multiple parameters of the same type
    func functionName(param1, param2 int) {}
    
    // return type declaration
    func functionName() int {
        return 42
    }
    
    // Can return multiple values at once
    func returnMulti() (int, string) {
        return 42, "foobar"
    }
    var x, str = returnMulti()
    
    // Return multiple named results simply by return
    func returnMulti2() (n int, s string) {
        n = 42
        s = "foobar"
        // n and s will be returned
        return
    }
    var x, str = returnMulti2()
    
    • 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
    函数作为值和闭包
    func main() {
        // assign a function to a name
        add := func(a, b int) int {
            return a + b
        }
        // use the name to call the function
        fmt.Println(add(3, 4))
    }
    
    // Closures, lexically scoped: Functions can access values that were
    // in scope when defining the function
    func scope() func() int{
        outer_var := 2
        foo := func() int { return outer_var}
        return foo
    }
    
    func another_scope() func() int{
        // won't compile because outer_var and foo not defined in this scope
        outer_var = 444
        return foo
    }
    
    
    // Closures
    func outer() (func() int, int) {
        outer_var := 2
        inner := func() int {
            outer_var += 99 // outer_var from outer scope is mutated.
            return outer_var
        }
        inner()
        return inner, outer_var // return inner func and mutated outer_var 101
    }
    
    • 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
    可变参数函数
    func main() {
    	fmt.Println(adder(1, 2, 3)) 	// 6
    	fmt.Println(adder(9, 9))	// 18
    
    	nums := []int{10, 20, 30}
    	fmt.Println(adder(nums...))	// 60
    }
    
    // By using ... before the type name of the last parameter you can indicate that it takes zero or more of those parameters.
    // The function is invoked like any other function except we can pass as many arguments as we want.
    func adder(args ...int) int {
    	total := 0
    	for _, v := range args { // Iterates over the arguments whatever the number.
    		total += v
    	}
    	return total
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    内置类型

    bool
    
    string
    
    int  int8  int16  int32  int64
    uint uint8 uint16 uint32 uint64 uintptr
    
    byte // alias for uint8
    
    rune // alias for int32 ~= a character (Unicode code point) - very Viking
    
    float32 float64
    
    complex64 complex128
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    所有Go的预声明标识符都定义在builtin包中。

    类型转换

    var i int = 42
    var f float64 = float64(i)
    var u uint = uint(f)
    
    // alternative syntax
    i := 42
    f := float64(i)
    u := uint(f)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    • 在每个源文件的顶部声明包
    • 可执行文件位于main包中
    • 约定:包的名称等于导入路径的最后一个部分(导入路径math/rand => 包rand
    • 大写标识符:导出的(可以从其他包中访问)
    • 小写标识符:私有的(不能从其他包中访问)

    控制结构

    判断
    func main() {
    	// Basic one
    	if x > 10 {
    		return x
    	} else if x == 10 {
    		return 10
    	} else {
    		return -x
    	}
    
    	// You can put one statement before the condition
    	if a := b + c; a < 42 {
    		return a
    	} else {
    		return a - 42
    	}
    
    	// Type assertion inside if
    	var val interface{} = "foo"
    	if str, ok := val.(string); ok {
    		fmt.Println(str)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    循环
        // There's only `for`, no `while`, no `until`
        for i := 1; i < 10; i++ {
        }
        for ; i < 10;  { // while - loop
        }
        for i < 10  { // you can omit semicolons if there is only a condition
        }
        for { // you can omit the condition ~ while (true)
        }
        
        // use break/continue on current loop
        // use break/continue with label on outer loop
    here:
        for i := 0; i < 2; i++ {
            for j := i + 1; j < 3; j++ {
                if i == 0 {
                    continue here
                }
                fmt.Println(j)
                if j == 2 {
                    break
                }
            }
        }
    
    there:
        for i := 0; i < 2; i++ {
            for j := i + 1; j < 3; j++ {
                if j == 1 {
                    continue
                }
                fmt.Println(j)
                if j == 2 {
                    break there
                }
            }
        }
    
    • 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
    条件
        // switch statement
        switch operatingSystem {
        case "darwin":
            fmt.Println("Mac OS Hipster")
            // cases break automatically, no fallthrough by default
        case "linux":
            fmt.Println("Linux Geek")
        default:
            // Windows, BSD, ...
            fmt.Println("Other")
        }
    
        // as with for and if, you can have an assignment statement before the switch value
        switch os := runtime.GOOS; os {
        case "darwin": ...
        }
    
        // you can also make comparisons in switch cases
        number := 42
        switch {
            case number < 42:
                fmt.Println("Smaller")
            case number == 42:
                fmt.Println("Equal")
            case number > 42:
                fmt.Println("Greater")
        }
    
        // cases can be presented in comma-separated lists
        var char byte = '?'
        switch char {
            case ' ', '?', '&', '=', '#', '+', '%':
                fmt.Println("Should escape")
        }
    
    • 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

    数组, 切片, 遍历

    数组
    var a [10]int // declare an int array with length 10. Array length is part of the type!
    a[3] = 42     // set elements
    i := a[3]     // read elements
    
    // declare and initialize
    var a = [2]int{1, 2}
    a := [2]int{1, 2} //shorthand
    a := [...]int{1, 2} // elipsis -> Compiler figures out array length
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    切片
    var a []int                              // declare a slice - similar to an array, but length is unspecified
    var a = []int {1, 2, 3, 4}               // declare and initialize a slice (backed by the array given implicitly)
    a := []int{1, 2, 3, 4}                   // shorthand
    chars := []string{0:"a", 2:"c", 1: "b"}  // ["a", "b", "c"]
    
    var b = a[lo:hi]	// creates a slice (view of the array) from index lo to hi-1
    var b = a[1:4]		// slice from index 1 to 3
    var b = a[:3]		// missing low index implies 0
    var b = a[3:]		// missing high index implies len(a)
    a =  append(a,17,3)	// append items to slice a
    c := append(a,b...)	// concatenate slices a and b
    
    // create a slice with make
    a = make([]byte, 5, 5)	// first arg length, second capacity
    a = make([]byte, 5)	// capacity is optional
    
    // create a slice from an array
    x := [3]string{"Лайка", "Белка", "Стрелка"}
    s := x[:] // a slice referencing the storage of x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    数组和切片的操作

    len(a) 返回数组/切片的长度。这是一个内置函数,而不是数组的属性/方法。

    // loop over an array/a slice
    for i, e := range a {
        // i is the index, e the element
    }
    
    // if you only need e:
    for _, e := range a {
        // e is the element
    }
    
    // ...and if you only need the index
    for i := range a {
    }
    
    // In Go pre-1.4, you'll get a compiler error if you're not using i and e.
    // Go 1.4 introduced a variable-free form, so that you can do this
    for range time.Tick(time.Second) {
        // do it once a sec
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    哈希表

    m := make(map[string]int)
    m["key"] = 42
    fmt.Println(m["key"])
    
    delete(m, "key")
    
    elem, ok := m["key"] // test if key "key" is present and retrieve it, if so
    
    // map literal
    var m = map[string]Vertex{
        "Bell Labs": {40.68433, -74.39967},
        "Google":    {37.42202, -122.08408},
    }
    
    // iterate over map content
    for key, value := range m {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    结构体

    Go中没有类,只有结构体。结构体可以拥有方法。

    // A struct is a type. It's also a collection of fields
    
    // Declaration
    type Vertex struct {
        X, Y float64
    }
    
    // Creating
    var v = Vertex{1, 2}
    var v = Vertex{X: 1, Y: 2} // Creates a struct by defining values with keys
    var v = []Vertex{{1,2},{5,2},{5,5}} // Initialize a slice of structs
    
    // Accessing members
    v.X = 4
    
    // You can declare methods on structs. The struct you want to declare the
    // method on (the receiving type) comes between the the func keyword and
    // the method name. The struct is copied on each method call(!)
    func (v Vertex) Abs() float64 {
        return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }
    
    // Call method
    v.Abs()
    
    // For mutating methods, you need to use a pointer (see below) to the Struct
    // as the type. With this, the struct value is not copied for the method call.
    func (v *Vertex) add(n float64) {
        v.X += n
        v.Y += n
    }
    
    • 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

    匿名结构体: 比使用 map[string]interface{} 更经济和更安全。

    point := struct {
    	X, Y int
    }{1, 2}
    
    • 1
    • 2
    • 3

    指针

    p := Vertex{1, 2}  // p is a Vertex
    q := &p            // q is a pointer to a Vertex
    r := &Vertex{1, 2} // r is also a pointer to a Vertex
    
    // The type of a pointer to a Vertex is *Vertex
    
    var s *Vertex = new(Vertex) // new creates a pointer to a new struct instance
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    接口

    // interface declaration
    type Awesomizer interface {
        Awesomize() string
    }
    
    // types do *not* declare to implement interfaces
    type Foo struct {}
    
    // instead, types implicitly satisfy an interface if they implement all required methods
    func (foo Foo) Awesomize() string {
        return "Awesome!"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    嵌入

    Go中没有子类化。相反,有接口和结构体嵌入。

    // ReadWriter implementations must satisfy both Reader and Writer
    type ReadWriter interface {
        Reader
        Writer
    }
    
    // Server exposes all the methods that Logger has
    type Server struct {
        Host string
        Port int
        *log.Logger
    }
    
    // initialize the embedded type the usual way
    server := &Server{"localhost", 80, log.New(...)}
    
    // methods implemented on the embedded struct are passed through
    server.Log(...) // calls server.Logger.Log(...)
    
    // the field name of the embedded type is its type name (in this case Logger)
    var logger *log.Logger = server.Logger
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    错误

    Go中没有异常处理。相反,可能产生错误的函数只是声明了一个额外的返回值,类型为error。这是error接口:

    // The error built-in interface type is the conventional interface for representing an error condition,
    // with the nil value representing no error.
    type error interface {
        Error() string
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这是一个示例:

    func sqrt(x float64) (float64, error) {
    	if x < 0 {
    		return 0, errors.New("negative value")
    	}
    	return math.Sqrt(x), nil
    }
    
    func main() {
    	val, err := sqrt(-1)
    	if err != nil {
    		// handle error
    		fmt.Println(err) // negative value
    		return
    	}
    	// All is good, use `val`.
    	fmt.Println(val)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    并发

    协程

    Goroutines是轻量级线程(由Go管理,而不是操作系统线程)。go f(a, b)启动一个新的goroutine来运行f(假设f是一个函数)。

    // just a function (which can be later started as a goroutine)
    func doStuff(s string) {
    }
    
    func main() {
        // using a named function in a goroutine
        go doStuff("foobar")
    
        // using an anonymous inner function in a goroutine
        go func (x int) {
            // function body goes here
        }(42)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    通道

    ch := make(chan int) // create a channel of type int
    ch <- 42             // Send a value to the channel ch.
    v := <-ch            // Receive a value from ch
    
    // Non-buffered channels block. Read blocks when no value is available, write blocks until there is a read.
    
    // Create a buffered channel. Writing to a buffered channels does not block if less than  unread values have been written.
    ch := make(chan int, 100)
    
    close(ch) // closes the channel (only sender should close)
    
    // read from channel and test if it has been closed
    v, ok := <-ch
    
    // if ok is false, channel has been closed
    
    // Read from channel until it is closed
    for i := range ch {
        fmt.Println(i)
    }
    
    // select blocks on multiple channel operations, if one unblocks, the corresponding case is executed
    func doStuff(channelOut, channelIn chan int) {
        select {
        case channelOut <- 42:
            fmt.Println("We could write to channelOut!")
        case x := <- channelIn:
            fmt.Println("We could read from channelIn")
        case <-time.After(time.Second * 1):
            fmt.Println("timeout")
        }
    }
    
    • 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
    通道原理
    • 向空通道发送会永远阻塞

      var c chan string
      c <- "Hello, World!"
      // fatal error: all goroutines are asleep - deadlock!
      
      • 1
      • 2
      • 3
    • 从空通道接收会永远阻塞。

      var c chan string
      fmt.Println(<-c)
      // fatal error: all goroutines are asleep - deadlock!
      
      • 1
      • 2
      • 3
    • 向已关闭的通道发送会引发恐慌。

      var c = make(chan string, 1)
      c <- "Hello, World!"
      close(c)
      c <- "Hello, Panic!"
      // panic: send on closed channel
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 从已关闭的通道接收会立即返回零值。

      var c = make(chan int, 2)
      c <- 1
      c <- 2
      close(c)
      for i := 0; i < 3; i++ {
          fmt.Printf("%d ", <-c)
      }
      // 1 2 0
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    打印

    fmt.Println("Hello, 你好, नमस्ते, Привет, ᎣᏏᏲ") // 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
    
    hellomsg := `
     "Hello" in Chinese is 你好 ('Ni Hao')
     "Hello" in Hindi is नमस्ते ('Namaste')
    ` // multi-line string literal, using back-tick at beginning and end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    反射

    类型切换

    类型切换类似于常规的switch语句,但类型切换中的情况指定要与给定接口值持有的值的类型进行比较的类型,而不是值。

    func do(i interface{}) {
    	switch v := i.(type) {
    	case int:
    		fmt.Printf("Twice %v is %v\n", v, v*2)
    	case string:
    		fmt.Printf("%q is %v bytes long\n", v, len(v))
    	default:
    		fmt.Printf("I don't know about type %T!\n", v)
    	}
    }
    
    func main() {
    	do(21)
    	do("hello")
    	do(true)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    片段

    文件嵌入

    Go程序可以使用"embed"包嵌入静态文件,如下所示:

    package main
    
    import (
    	"embed"
    	"log"
    	"net/http"
    )
    
    // content holds the static content (2 files) for the web server.
    //go:embed a.txt b.txt
    var content embed.FS
    
    func main() {
    	http.Handle("/", http.FileServer(http.FS(content)))
    	log.Fatal(http.ListenAndServe(":8080", nil))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    完整的Playground示例

    HTTP服务器

    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() {
        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)
    // }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    RabbitMQ核心总结
    Mybatis-Plus--逻辑删除的用法
    《庆余年》角色穿越高考:谁将笑傲现代考场?
    SpringBoot实用开发篇复习4(实用开发篇完)
    c++多态
    centos上安装clickhouse和启动使用
    【阿旭机器学习实战】【17】KMeans聚类算法中如何选择合适的聚类个数K
    TARJAN复习 求强连通分量、割点、桥
    总结使人进步,4句真章的理解和实践
    利用CompletableFuture提高接口的响应速度
  • 原文地址:https://blog.csdn.net/csjds/article/details/133501662