该备忘单提供了帮助您使用 Golang 的基本语法和方法。

入门

hello.go

1
2
3
4
5
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}

直接运行

1
2
$ go run hello.go
Hello, world!

或者在 Go repl 中尝试一,go 命令参考

变量

1
2
3
4
5
6
7
var s1 string
s1 = "Learn Go!"
// 一次声明多个变量
var b, c int = 1, 2
var d = true
// 匿名赋值
_ , e = 10, 20

简短声明

1
2
3
s1 := "Learn Go!"        // string
b, c := 1, 2 // int
d := true // bool

参见:基本类型

函数

1
2
3
4
5
6
7
8
9
10
package main
import "fmt"
// 程序的入口点
func main() {
fmt.Println("Hello world!")
say("Hello Go!")
}
func say(message string) {
fmt.Println("You said: ", message)
}

参见:函数(Functions)

注释

1
2
3
// 单行注释
/* 这是
多行注释 */

if 语句

1
2
3
if true {
fmt.Println("Yes!")
}

参见:条件控制

Golang 基本类型

字符串 Strings

1
2
3
4
5
6
7
s1 := "Hello" + "World"
s2 := `A "raw" string literal
can include line breaks.`
// 输出:10
fmt.Println(len(s1))
// 输出:Hello
fmt.Println(string(s1[0:5]))

字符串的类型为 字符串

数字 Numbers

1
2
3
4
5
6
num := 3             // int
num := 3. // float64
num := 3 + 4i // complex128
num := byte('a') // byte (alias: uint8)
var u uint = 7 // uint (unsigned)
var p float32 = 22.7 // 32-bit float

操作符 Operators

1
2
3
4
x := 5
x++
fmt.Println("x + 4 =", x + 4)
fmt.Println("x * 4 =", x * 4)

参见:更多操作符

布尔值 Booleans

1
2
isTrue   := true
isFalse := false

操作符

1
2
3
4
5
fmt.Println(true && true)   // true 
fmt.Println(true && false) // false
fmt.Println(true || true) // true
fmt.Println(true || false) // true
fmt.Println(!true) // false

参见:更多操作符

数组 Arrays

1
2
3
4
┌────┬────┬────┬────┬─────┬─────┐
| 2 | 3 | 5 | 7 | 11 | 13 |
└────┴────┴────┴────┴─────┴─────┘
0 1 2 3 4 5

1
2
3
4
5
6
primes := [...]int{2, 3, 5, 7, 11, 13}
fmt.Println(len(primes)) // => 6
// 输出:[2 3 5 7 11 13]
fmt.Println(primes)
// 与 [:3] 相同,输出:[2 3 5]
fmt.Println(primes[0:3])

1
2
3
4
5
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1]) //=> Hello World
fmt.Println(a) // => [Hello World]

2d array

1
2
3
4
5
6
7
8
var twoDimension [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoDimension[i][j] = i + j
}
}
// => 2d: [[0 1 2] [1 2 3]]
fmt.Println("2d: ", twoDimension)

指针(Pointers)

1
2
3
4
5
6
7
8
9
10
11
12
13
func main () {
b := *getPointer()
fmt.Println("Value is", b)
}

func getPointer () (myPointer *int) {
a := 234
return &a
}
//申明指针的时候,如果没有指向某个变量,默认值为nil
//不能直接进行操作,包括读写
var p *int
*p = 123 // panic nil pointer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//而用new返回的是有默认值的指针, 为数据类型的默认值
func main(){
//有一块内存存放了10,它的地址由系统自动分配,别名是a
a := 10
//内存存放的10变成了20
a = 20
var p *int
p = &a //或者直接写 p := &a
//上面的p是一个指针,通过 *p 的方式同样可以访问 变量a指向 的内存

/*当你动态申请内存的时候,指针的存在意义之一就被体现出来了*/
ptr := new(int)
//申请了一块内存空间,没有办法指定别名,new()返回内存地址,用指针接收
//此时并没有变量能直接指向这块内存,所以只能通过内存地址来访问
}

参见:指针(Pointers)

切片(Slices)

1
2
3
4
5
6
7
8
9
10
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println(s)
fmt.Println(s[1])
fmt.Println(len(s))
fmt.Println(s[1:3])
slice := []int{2, 3, 4}

另见:切片示例

常量(Constants)

1
2
3
4
const s string = "constant"
const Phi = 1.618
const n = 500000000
const d = 3e20 / n

常量声明可以使用 iota常量生成器 初始化,它用于
生成一组以相似规则初始化的常量,但是不用每行都
写一遍初始化表达式。
注意:

  1. 在一个const声明语句中,在第一个声明的常量所在的行,iota被置为0,然后在每一个有常量声明的行加一。
  2. 写在同一行的值是相同的
1
2
3
4
5
6
const (
a = iota
b
c
)
// a = 0, b = 1, c = 2

类型转换

Go语言中不允许隐式转换,所有类型转换必须显式声明(强制转换),而且转换只能发生在两种相互兼容的类型之间。

1
2
3
4
5
i := 90
f := float64(i)
u := uint(i)
// 将等于字符Z
s := string(i)

字符串与其他类型的相互转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 字符串转其他类型
str := "90"
// 整数类型
i, err := strconv.Atoi(str)
if err != nil {
fmt.Println("转换错误:", err)
} else {
fmt.Println(i)
}
// 浮点类型
f, err := strconv.ParseFloat(str, 64)
// []byte 类型
bytes := []byte(str)
// 其他类型转字符串
str = strconv.Itoa(i)
str = strconv.FormatFloat(f, 'f', 2, 64)
str = string(bytes[:])

Golang 字符串

字符串函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import (
"fmt"
s "strings"
)
func main() {
/* 需要将字符串导入为 s */
fmt.Println(s.Contains("test", "e"))
/* 内置 */
fmt.Println(len("hello")) // => 5
// 输出: 101
fmt.Println("hello"[1])
// 输出: e
fmt.Println(string("hello"[1]))
}

fmt.Printf

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
package main
import (
"fmt"
"os"
)
type point struct {
x, y int
}
func main() {
p := point{1, 2}
fmt.Printf("%v\n", p) // => {1 2}
fmt.Printf("%+v\n", p) // => {x:1 y:2}
fmt.Printf("%#v\n", p) // => main.point{x:1, y:2}
fmt.Printf("%T\n", p) // => main.point
fmt.Printf("%t\n", true) // => TRUE
fmt.Printf("%d\n", 123) // => 123
fmt.Printf("%b\n", 14) // => 1110
fmt.Printf("%c\n", 33) // => !
fmt.Printf("%x\n", 456) // => 1c8
fmt.Printf("%f\n", 78.9) // => 78.9
fmt.Printf("%e\n", 123400000.0) // => 1.23E+08
fmt.Printf("%E\n", 123400000.0) // => 1.23E+08
fmt.Printf("%s\n", "\"string\"") // => "string"
fmt.Printf("%q\n", "\"string\"") // => "\"string\""
fmt.Printf("%x\n", "hex this") // => 6.86578E+15
fmt.Printf("%p\n", &p) // => 0xc00002c040
fmt.Printf("|%6d|%6d|\n", 12, 345) // => | 12| 345|
fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45) // => | 1.20| 3.45|
fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45) // => |1.20 |3.45 |
fmt.Printf("|%6s|%6s|\n", "foo", "b") // => | foo| b|
fmt.Printf("|%-6s|%-6s|\n", "foo", "b") // => |foo |b |
s := fmt.Sprintf("a %s", "string")
fmt.Println(s)
fmt.Fprintf(os.Stderr, "an %s\n", "error")
}

另见:fmt

函数实例

实例 Result
Contains(“test”, “es”) true
Count(“test”, “t”) 2
HasPrefix(“test”, “te”) true
HasSuffix(“test”, “st”) true
Index(“test”, “e”) 1
Join([]string{“a”, “b”}, “-“) a-b
Repeat(“a”, 5) aaaaa
Replace(“foo”, “o”, “0”, -1) f00
Replace(“foo”, “o”, “0”, 1) f0o
Split(“a-b-c-d-e”, “-“) [a b c d e]
ToLower(“TEST”) test
ToUpper(“test”) TEST

Golang 条件控制

有条件的

1
2
3
4
5
6
7
8
a := 10
if a > 20 {
fmt.Println(">")
} else if a < 20 {
fmt.Println("<")
} else {
fmt.Println("=")
}

if 中的语句

1
2
3
4
x := "hello go!"
if count := len(x); count > 0 {
fmt.Println("Yes")
}

1
2
3
if _, err := doThing(); err != nil {
fmt.Println("Uh oh")
}

Switch

1
2
3
4
5
6
7
8
9
10
11
12
x := 42.0
switch x {
case 0:
case 1, 2:
fmt.Println("Multiple matches")
case 42: // Don't "fall through".
fmt.Println("reached")
case 43:
fmt.Println("Unreached")
default:
fmt.Println("Optional")
}

参见:Switch

For loop

1
2
3
for i := 0; i <= 10; i++ {
fmt.Println("i: ", i)
}

对于 Range 循环

1
2
3
4
5
6
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)

For 循环

1
2
3
4
5
i := 1
for i <= 3 {
fmt.Println(i)
i++
}

Continue 关键字

1
2
3
4
5
6
for i := 0; i <= 5; i++ {
if i % 2 == 0 {
continue
}
fmt.Println(i)
}

Break 关键字

1
2
3
4
for {
fmt.Println("loop")
break
}

Golang 结构和Maps

定义

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import (
"fmt"
)
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X, v.Y) // => 4 2
}

参见:结构(Structs)

字面量

1
2
3
4
5
v := Vertex{X: 1, Y: 2}
// Field names can be omitted
v := Vertex{1, 2}
// Y is implicit
v := Vertex{X: 1}

您还可以输入字段名

Maps

1
2
3
4
5
6
7
8
9
10
11
12
13
m := make(map[string]int)
m["k1"] = 7
m["k2"] = 13
fmt.Println(m) // => map[k1:7 k2:13]
v1 := m["k1"]
fmt.Println(v1) // => 7
fmt.Println(len(m)) // => 2
delete(m, "k2")
fmt.Println(m) // => map[k1:7]
_, prs := m["k2"]
fmt.Println(prs) // => false
n := map[string]int{"foo": 1, "bar": 2}
fmt.Println(n) // => map[bar:2 foo:1]

指向结构的指针

1
2
v := &Vertex{1, 2}
v.X = 2

Doing v.X is the same as doing (*v).X, when v is a pointer.

Golang 函数

多个参数

1
2
3
4
5
6
7
8
func plus(a int, b int) int {
return a + b
}
func plusPlus(a, b, c int) int {
return a + b + c
}
fmt.Println(plus(1, 2))
fmt.Println(plusPlus(1, 2, 3))

多返回值

1
2
3
4
5
6
func vals() (int, int) {
return 3, 7
}
a, b := vals()
fmt.Println(a) // => 3
fmt.Println(b) // => 7

匿名函数

1
2
3
4
5
6
r1, r2 := func() (string, string) {
x := []string{"hello", "world"}
return x[0], x[1]
}()
// => hello world
fmt.Println(r1, r2)

命名返回值

1
2
3
4
5
6
7
8
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
x, y := split(17)
fmt.Println(x) // => 7
fmt.Println(y) // => 10

可变参数函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func sum(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
sum(1, 2) // => [1 2] 3
sum(1, 2, 3) // => [1 2 3] 6
nums := []int{1, 2, 3, 4}
sum(nums...) // => [1 2 3 4] 10
// 不定参在内存中是连续存储的
// 不定参内部再传递的时候,参数也要是不定的

初始化函数

1
import --> const --> var --> init()

1
2
3
4
5
6
7
8
9
10
var num = setNumber()
func setNumber() int {
return 42
}
func init() {
num = 0
}
func main() {
fmt.Println(num) // => 0
}

作为值的函数

1
2
3
4
5
6
7
8
func main() {
// 将函数赋给名称
add := func(a, b int) int {
return a + b
}
// 使用名称调用函数
fmt.Println(add(3, 4)) // => 7
}

闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func outer() (func() int, int) {
outer_var := 2
inner := func() int {
outer_var += 99
return outer_var
}
inner()
return inner, outer_var
}
inner, val := outer()
fmt.Println(val)
// => 101
fmt.Println(inner())
// => 200,这里涉及到golang中闭包和内存逃逸的概念,inner()实际上执行了两次,outer()中一次,fmt又一次,
//但为什么是200呢,编译器不能确定outer_var在后续会不会使用,
//所以outer_var不会随着outer()结束而释放它的栈(Stack)空间,
//而会‘逃逸到’堆(Heap)上,那么第二次的inner()中outer_var就会是101。

关闭 1

1
2
3
4
5
6
7
func scope() func() int{
outer_var := 2
foo := func() int {return outer_var}
return foo
}
// Outpus: 2
fmt.Println(scope()())

Golang 包(Packages)

导入

1
2
import "fmt"
import "math/rand"

等同于

1
2
3
4
import (
"fmt" // 给 fmt.Println
"math/rand" // 给 rand.Intn
)

另见:导入

别名

1
import r "math/rand"

1
2
3
4
import (
"fmt"
r "math/rand"
)

1
r.Intn()

Packages

1
2
3
4
package main
// 一个内部包只能被另一个包导入
// 那是在以内部目录的父级为根的树内
package internal

另见:内部包

导出名称

1
2
3
4
// 以大写字母开头
func Hello () {
···
}

另见:导出的名称

Golang 并发

协程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
import (
"fmt"
"time"
)
func f(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
}
}
func main() {
f("direct")
go f("goroutine")
go func(msg string) {
fmt.Println(msg)
}("going")
time.Sleep(time.Second)
fmt.Println("done")
}

参见:Goroutines, Channels

WaitGroup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import (
"fmt"
"sync"
"time"
)
func w(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("%d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("%d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go w(i, &wg)
}
wg.Wait()
}

参见:WaitGroup

Closing channels

1
2
3
4
ch <- 1
ch <- 2
ch <- 3
close(ch) // 关闭频道

1
2
3
4
// 迭代通道直到关闭
for i := range ch {
···
}

1
2
// Closed if `ok == false`
v, ok := <- ch

参见:范围和关闭

缓冲通道

1
2
3
4
5
6
ch := make(chan int, 2)
ch <- 1
ch <- 2
ch <- 3
// 致命错误:
// 所有 goroutine 都处于休眠状态 - 死锁

参见:缓冲通道

Golang 错误控制

延迟函数

1
2
3
4
5
6
func main() {
defer func() {
fmt.Println("Done")
}()
fmt.Println("Working...")
}

Lambda defer

1
2
3
4
5
6
7
8
func main() {
var d = int64(0)
defer func(d *int64) {
fmt.Printf("& %v Unix Sec\n", *d)
}(&d)
fmt.Print("Done ")
d = time.Now().Unix()
}

defer 函数使用当前值d,除非我们使用指针在 main 末尾获取最终值

Defer

1
2
3
4
func main() {
defer fmt.Println("Done")
fmt.Println("Working...")
}

参见:Defer, panic and recover

Golang 方法(Methods)

接收器

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
//Go语言中的方法(Method)是一种作用于特定类型变量的函数。
//这种特定类型变量叫做接收者(Receiver)。
//接收者的概念就类似于其他语言中的 this 或者 self。
//方法的定义格式如下:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
// 其中,
// 1.接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名
//的第一个小写字母,而不是self、this之类的命名。例如,Person类型的接收者变量
// 应该命名为 p,Connector类型的接收者变量应该命名为c等。
// 2.接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
// 3.方法名、参数列表、返回参数:具体格式与函数定义相同。
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) valuechange() float64 {
v.X += 1
return v.X
}
func (v *Vertex) pointerchange() float64 {
v.X += 1
return v.X
}
func main() {
v := Vertex{1, 2}
v.Abs()

v = Vertex{1, 2}
fmt.Println(v.valuechange()) // 2
fmt.Println(v) // {1 2}

v = Vertex{1, 2}
fmt.Println(v.pointerchange())// 2
fmt.Println(v) // {2 2}
}
//如果在方法里修改receiver的值要对caller生效,使用 pointer receiver。

参见:Methods指针接收器

方法表达式

方法表达式相当于提供一种语法将类型方法调用显式地转换为函数调用,接收者(receiver)必须显式地传递进去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func (t T) Get(){
return t.a
}
func (t *T) Set(i int){
t.a = i
}
//表达式`T.Get`和`(*T).Set`被称为方法表达式,
//需要注意的是在方法表达式中编译器不会做自动转换。
//值调用会自动转换,表达式调用则不会,例如:
type Data struct{}
func (Data) TestValue () {}
func (*Data) TestPointer () {}
//声明一个类型变量a
var a Data= struct{}{}
//表达式调用编译器不会进行自动转换
Data.TestValue(a)
//Data.TestValue(&a)
(*Data).TestPointer (&a)
//Data.TestPointer(&a) //type Data has no method TestPointer
//值调用编译器会进行自动转换
y : = (&a).TestValue //编译器帮助转换a.TestValue
g : = a.TestPointer //会转换为(&a).TestPointer

组合结构的方法集

内嵌字段的访问不需要使用全路径,只要保证命名是唯一的就可以,尽量避免同名。如果外层字段和内层字段有相同的方法,则使用简化模式访问外层方法会覆盖内层的方法。

1
2
3
4
5
6
7
8
9
x : = X{a: 1} 
y : = Y{
X : x ,
b : 2 ,
}
z : = z {
Y : y ,
c : 3 ,
}//组合结构,内嵌字段

组合结构的方法集有如下规则:

  • 若类型 T 包含匿名字段 S ,则 T 的方法集包含S的方法集
  • 若类型 T 包含匿名字段 S ,则 T 的方法集包含 S 和S方法集
  • 不管类型 T 中嵌入的匿名字段是 S 还是 S ,T 方法集总是包含 S 和 *S 方法集

Golang 接口(Interfaces)

基本接口(Interfaces)

1
2
3
4
type Shape interface {
Area() float64
Perimeter() float64
}

结构(Struct)

1
2
3
type Rectangle struct {
Length, Width float64
}

结构 Rectangle 通过实现其所有方法隐式实现接口 Shape

方法(Methods)

1
2
3
4
5
6
func (r Rectangle) Area() float64 {
return r.Length * r.Width
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Length + r.Width)
}

Shape 中定义的方法在Rectangle中实现

接口实例

1
2
3
4
func main() {
var r Shape = Rectangle{Length: 3, Width: 4}
fmt.Printf("Type of r: %T, Area: %v, Perimeter: %v.", r, r.Area(), r.Perimeter())
}

Golang Embed (Go version >= 1.16)

嵌入为string

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
_ "embed"
"fmt"
)

//go:embed version.txt
var version string

func main() {
fmt.Printf("version %q\n", version)
}

嵌入为[]byte

1
2
3
4
5
6
7
8
9
10
11
12
package main
import (
_ "embed"
"fmt"
)

//go:embed version.txt
var versionByte []byte

func main() {
fmt.Printf("version %q\n", string(versionByte))
}

嵌入为embed.FS

1
2
3
4
5
6
//go:embed hello.txt
var f embed.FS
func main() {
data, _ := f.ReadFile("hello.txt")
fmt.Println(string(data))
}

嵌入多个文件

1
2
3
4
5
6
7
8
9
//go:embed hello.txt
//go:embed hello2.txt
var f embed.FS
func main() {
data, _ := f.ReadFile("hello.txt")
fmt.Println(string(data))
data, _ = f.ReadFile("hello2.txt")
fmt.Println(string(data))
}

嵌入子文件夹下的文件

1
2
3
4
5
6
7
8
//go:embed p/hello.txt p/hello2.txt
var f embed.FS
func main() {
data, _ := f.ReadFile("p/hello.txt")
fmt.Println(string(data))
data, _ = f.ReadFile("p/hello2.txt")
fmt.Println(string(data))
}

同一个文件嵌入为多个变量

1
2
3
4
5
6
7
8
//go:embed hello.txt
var s string
//go:embed hello.txt
var s2 string
func main() {
fmt.Println(s)
fmt.Println(s2)
}

匹配模式

1
2
3
4
5
6
7
8
//go:embed p/*
var f embed.FS
func main() {
data, _ := f.ReadFile("p/.hello.txt")
fmt.Println(string(data))
data, _ = f.ReadFile("p/q/.hi.txt") // 没有嵌入 p/q/.hi.txt
fmt.Println(string(data))
}

Golang 泛型 (Go version >= 1.18)

泛型类型

1
2
3
4
type S[T int|float32|float64 ] []T
┬ ────────┬────────
┆ ╰─── 2. 类型约束
╰────────────── 1. 类型形参

可以使用类型实参 int 或 string 实例化

1
2
3
4
5
6
type MyMap[K int|string, V float32 | float64] map[K]V

var a MyMap[string, float64] = map[string]float64{
"jack_score": 9.6,
"bob_score": 8.4,
}

泛型函数

任意类型

1
2
3
func Add[T any](a,b T) T {
return a+b
}

对类型进行约束

1
2
3
func Add[T string | int | int8](a,b T) T {
return a+b
}

类型嵌套

1
2
3
4
5
6
7
type WowStruct[T int | float32, S []T] struct {
Data S
MaxValue T
MinValue T
}

var ws WowStruct[int, []int]

泛型函数中进行类型声明 (go version >= 1.20)

1
2
3
4
func F[T1 any]() {
type x struct{}
type y = x
}

泛型约束

通过接口实现

1
2
3
4
5
6
7
type Addable interface{
type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128, string
}

func Add[T Addable](a,b T) T {
return a+b
}

使用 ~ 符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Int interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}

type Uint interface {
~uint | ~uint8 | ~uint16 | ~uint32
}
type Float interface {
~float32 | ~float64
}

type Slice[T Int | Uint | Float] []T

var s Slice[int] // 正确

type MyInt int
var s2 Slice[MyInt] // MyInt底层类型是int,所以可以用于实例化

type MyMyInt MyInt
var s3 Slice[MyMyInt] // 正确。MyMyInt 虽然基于 MyInt ,但底层类型也是int,所以也能用于实例化

type MyFloat32 float32 // 正确
var s4 Slice[MyFloat32]

使用 ~ 时的限制:

  1. ~后面的类型不能为接口
  2. ~后面的类型必须为基本类型

泛型 Receiver

定义普通类型支持泛型

1
2
3
4
5
6
7
8
9
type MySlice[T int | float32] []T

func (s MySlice[T]) Sum() T {
var sum T
for _, value := range s {
sum += value
}
return sum
}

结构体支持泛型

1
2
3
4
5
6
7
type A[T int | float32 | float64] struct {
}

func (receiver A[T]) Add(a T, b T) T {
return a + b
}

泛型接口

1
2
3
4
5
6
7
8
9
10
type Uint interface { // 接口 Uint 中有类型,所以是一般接口
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

type ReadWriter interface { // ReadWriter 接口既有方法也有类型,所以是一般接口
~string | ~[]rune

Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}

一般接口类型不能用来定义变量,只能用于泛型的类型约束中

杂项

关键字(Keywords)

  • break
  • default
  • func
  • interface
  • select
  • case
  • defer
  • go
  • map
  • struct
  • chan
  • else
  • goto
  • package
  • switch
  • const
  • fallthrough
  • if
  • range
  • type
  • continue
  • for
  • import
  • return
  • var

运算符和标点符号

+ & += &= && == != ( )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= ++ = := , ;
% >> %= >>= -- ! ... . :
&^ &^=

Go 命令

Go 编译器命令

:-
go command [参数] go 命令 [参数]
go build 编译包和依赖包
go clean 移除对象和缓存文件
go doc 显示包的文档
go env 打印go的环境变量信息
go bug 报告bug
go fix 更新包使用新的api
go fmt 格式规范化代码
go generate 通过处理资源生成go文件
go get 下载并安装包及其依赖
go install 编译和安装包及其依赖
go list 列出所有包
go run 编译和运行go程序
go test 测试
go tool 运行给定的go工具
go version 显示go当前版本
go vet 发现代码中可能的错误

ENV

:-
GOOS 编译系统
GOARCH 编译arch
GO111MODULE gomod开关
GOPROXY go代理 https://goproxy.io https://goproxy.cn https://mirrors.aliyun.com/goproxy/
GOSSAFUNC 生成 SSA.html 文件,展示代码优化的每一步 GOSSAFUNC=func_name go build

Module

:-
go mod init 初始化当前文件夹,创建go.mod文件
go mod download 下载依赖的module到本地
go mod tidy 增加缺少的module,删除无用的module
go mod vendor 将依赖复制到vendor下
文件 go.mod 依赖列表和版本约束
文件 go.sum 记录 module 文件 hash 值,用于安全校验

另见


评论
avatar
竹山一叶
技术分享 个人心得
Follow Me
公告
欢迎光临小站,这里是我日常工作和学习中收集和整理的总结,希望能对你有所帮助:)

本站的内容经过个人加工总结而来,也参考了网友们分享的资料,如有侵权,请第一时间联系我,我将及时进行修改或删除😊
目录
  1. 1. 入门
    1. 1.1. hello.go
    2. 1.2. 变量
    3. 1.3. 函数
    4. 1.4. 注释
    5. 1.5. if 语句
  2. 2. Golang 基本类型
    1. 2.1. 字符串 Strings
    2. 2.2. 数字 Numbers
      1. 2.2.1. 操作符 Operators
    3. 2.3. 布尔值 Booleans
      1. 2.3.1. 操作符
    4. 2.4. 数组 Arrays
      1. 2.4.1. 2d array
    5. 2.5. 指针(Pointers)
    6. 2.6. 切片(Slices)
    7. 2.7. 常量(Constants)
    8. 2.8. 类型转换
      1. 2.8.1. 字符串与其他类型的相互转换
  3. 3. Golang 字符串
    1. 3.1. 字符串函数
    2. 3.2. fmt.Printf
    3. 3.3. 函数实例
  4. 4. Golang 条件控制
    1. 4.1. 有条件的
    2. 4.2. if 中的语句
    3. 4.3. Switch
    4. 4.4. For loop
    5. 4.5. 对于 Range 循环
    6. 4.6. For 循环
    7. 4.7. Continue 关键字
    8. 4.8. Break 关键字
  5. 5. Golang 结构和Maps
    1. 5.1. 定义
    2. 5.2. 字面量
    3. 5.3. Maps
    4. 5.4. 指向结构的指针
  6. 6. Golang 函数
    1. 6.1. 多个参数
    2. 6.2. 多返回值
    3. 6.3. 匿名函数
    4. 6.4. 命名返回值
    5. 6.5. 可变参数函数
    6. 6.6. 初始化函数
    7. 6.7. 作为值的函数
    8. 6.8. 闭包
    9. 6.9. 关闭 1
  7. 7. Golang 包(Packages)
    1. 7.1. 导入
      1. 7.1.1. 等同于
    2. 7.2. 别名
    3. 7.3. Packages
    4. 7.4. 导出名称
  8. 8. Golang 并发
    1. 8.1. 协程
    2. 8.2. WaitGroup
    3. 8.3. Closing channels
    4. 8.4. 缓冲通道
  9. 9. Golang 错误控制
    1. 9.1. 延迟函数
    2. 9.2. Lambda defer
    3. 9.3. Defer
  10. 10. Golang 方法(Methods)
    1. 10.1. 接收器
    2. 10.2. 方法表达式
      1. 10.2.1. 组合结构的方法集
  11. 11. Golang 接口(Interfaces)
    1. 11.1. 基本接口(Interfaces)
    2. 11.2. 结构(Struct)
    3. 11.3. 方法(Methods)
    4. 11.4. 接口实例
  12. 12. Golang Embed (Go version >= 1.16)
    1. 12.1. 嵌入为string
    2. 12.2. 嵌入为[]byte
    3. 12.3. 嵌入为embed.FS
    4. 12.4. 嵌入多个文件
    5. 12.5. 嵌入子文件夹下的文件
    6. 12.6. 同一个文件嵌入为多个变量
    7. 12.7. 匹配模式
  13. 13. Golang 泛型 (Go version >= 1.18)
    1. 13.1. 泛型类型
    2. 13.2. 泛型函数
    3. 13.3. 泛型约束
    4. 13.4. 泛型 Receiver
    5. 13.5. 泛型接口
  14. 14. 杂项
    1. 14.1. 关键字(Keywords)
    2. 14.2. 运算符和标点符号
  15. 15. Go 命令
    1. 15.1. Go 编译器命令
    2. 15.2. ENV
    3. 15.3. Module
  16. 16. 另见
最新文章
网站资讯
文章数目 :
436
已运行时间 :
本站总字数 :
431.5k
本站访客数 :
本站总访问量 :
最后更新时间 :
文章归档文章分类文章标签复制本文标题复制本文地址
随便逛逛