0%

Go 学习笔记

常用packages

fmt

Printf

verb 描述
%v 输出值
%+v 输出键-值
%#v 输出包名、类型名、键-值
%T 类型
%% 百分号
%t 布尔值
%b 二进制的值
%c Unicode编码的字符
%d 十进制的值
%o 八进制的值
%x或%X 十六进制的值
%U 十六进制表示的Unicode值
%s 字符串
%p 地址
%f 浮点数,默认精度是小数点后6位
%e 浮点数,科学计数法,默认精度是小数点后6位
%g 浮点数,有效数字,尽可能地输出所有位数
+ 添加正负号
- 设置宽度时默认在左边补全空格,该符号可设置在右边补全空格
0 用0代替空格进行补全
# 对于八进制,十六进制等,加上提示符,如八进制为0,十六进制为0x

%f 可指定浮点数的宽度和精度

1
2
3
4
5
%f     default width, default precision
%9f width 9, default precision
%.2f default width, precision 2
%9.2f width 9, precision 2
%9.f width 9, precision 0

%g 可指定浮点数的有效数字位数,对于12.345,%.3g 将输出 12.3

若要能对某个自定义类型输出,只要对它定义String() string方法即可:

1
2
3
4
5
6
func (t *T) String() string {
return fmt.Sprintf("%d/%g/%q", t.a, t.b, t.c)
}
// 只有当t是*T类型时,才会调用上面那个函数
// 若要当t是T类型和*T类型都都调用上面那个函数,需要将上面的*T改成T
fmt.Printf("%v\n", t)

log

会将内容输出到stderr,且会增加一些信息(如日期时间)。

json

注意只有当结构体内的成员是公开时,才能在Marshal的时候被识别,成为json文件的一部分。

进行Unmarshal时,假如json中有的字段而结构体没有,则这个字段会被忽略,不影响解析。也就是说,可进行json文件的部分解析。同理,假如Marshal时,结构体中的字段不想转到JSON文件中,可以将其tag设置为”-“。

解析时,结构体的某个字段的匹配优先级为tag -> 导出名精确匹配 -> 导出名模糊匹配。

omitempty表示当字段为零值时忽略它,而tag为”-“表示直接忽略它。

UnmarshalText函数和UnmarshalJSON函数的区别是什么?

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
// 当不知道json文件的格式时,可这样解析
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
var f interface{}
err := json.Unmarshal(b, &f)
if err == nil {
m := f.(map[string]interface{})
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case float64:
fmt.Println(k, "is float64", vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I don't know how to handle")
}
}
}

// 若将结构体的成员设置为指针类型,则若json不存在相应的字段,则为nil
type IncomingMessage struct {
Cmd *Command
Msg *Message
}


// 对于流的Encoders和Decoders
de := json.NewDecoder(os.Stdin)
enc := json.NewEncoder(os.Stdout)
for {
var v map[string]interface{}
if err := dec.Decode(&v); err != nil {
log.Println(err)
return
}
for k := range v {
if k != "Name" {
delete(v, k)
}
}
if err := enc.Encode(&v); err != nil {
log.Println(err)
}
}

Basic component

package | import

1
2
3
4
5
6
7
8
9
10
11
12
// 任何go程序都是由package组成,首个非空单词必须是package
package main

// 单个import
import "os"

// 多个import
import (
"fmt"
"math"
)

functions

形式:形参的标识符在前,类型在后;返回值放在最后面

1
2
3
4
func add(x int, y int) int {
return x + y
}

这样做的主要原因是为了提高易读性,特别是在涉及函数变量(函数指针)的时候

1
2
3
f func(func(int,int) int, int) int
f func(func(int,int) int, int) func(int, int) int

同类型的形参可简写

1
2
3
4
func add(x, y int) int {
return x + y
}

返回值可有多个

1
2
3
4
5
6
7
8
9
func swap(x, y string) (string, string) {
return y, x
}

func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}

可给返回值命名,且return可简写,注意不要在长函数中简写,因为这样会降低可读性

1
2
3
4
5
6
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}

variables | constants | types

首字母大写的变量称为exported name

1
2
3
4
5
type A struct {
Address string // exported name
cost int
}

申明格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 单句
var c, python, java bool

// 有赋初值时,可省略类型名
var c, python, java = true, false, "no!"
d := 3

// 块
var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)

当省略类型名时,编译器会自动推测,推测规则为:

  • 右边是变量,则和变量的类型相同
  • 右边是常量,则有可能是int, float64, complex128

基础类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool

string

int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr

byte // alias for uint8

rune // alias for int32
// represents a Unicode code point

float32 float64

complex64 complex128

申明的变量假如没有被显式赋初值,则会被赋zero value,即数值为0,布尔类型为false,字符串为””,指针为nil

申明常量,用const,不能用:=,因为:=是和var关联的,而var代表变量。

1
2
3
4
5
func main() {
const World = "世界"
fmt.Println("Hello", World)
}

没有隐式类型转换,必须显式类型转换。

类型断言,用于将空接口转换为普通类型。类型断言又分为安全类型断言和非安全类型断言。

  • <目标类型的值>,<布尔参数> := <表达式>.(目标类型) // 安全类型断言
  • <目标类型的值> := <表达式>.(目标类型) // 非安全类型断言

在函数体内,变量申明了但不使用会报错。同理,import的package没用的话也会报错。

Go在函数体中定义了一个变量,然后将它的指针返回是合法的,比如:

1
2
3
4
5
6
7
8
9
10
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := File{fd, name, nil, 0}
return &f
// 或者将上面两行简写成return &File{fd: fd, name: name}
// 不难发现new(File)和&File{}是等价的
}

More types

Pointers

指针,和C不一样,Go没有指针的算术运算,即不支持p = p + 10这样的语句

Structs

结构体,可将(*p).X写成p.X

1
2
3
4
5
6
7
8
9
10
11
12
type Vertex struct {
X int
Y int
}

var (
v1 = Vertex{1, 2} // has type Vertex
v2 = Vertex{X: 1} // Y:0 is implicit
v3 = Vertex{} // X:0 and Y:0
p = &Vertex{1, 2} // has type *Vertex
)

Arrays

In Go:

  • Arrays are values. Assigning one array to another copies all the elements.
  • In particular, if you pass an array to a function, it will receive a copy of the array, not a pointer to it.
  • The size of an array is part of its type. The types [10]int and [20]int are distinct.
1
2
3
4
5
6
7
8
9
10
11
func main() {
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)

primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)
}

假如想要像C那样只传数组的地址,可以这样写:

1
2
3
4
5
6
7
8
9
10
func Sum(a *[3]float64) (sum float64) {
for _, v := range *a {
sum += v
}
return
}

array := [...]float64{7.0, 8.5, 9.1}
x := Sum(&array) // Note the explicit address-of operator

虽然可以这么写,但不推荐,因为更优雅的方法是使用slices。

Slices

切片,本身不存储实际数据,类似于引用

1
2
3
4
5
6
7
func main() {
primes := [6]int{2, 3, 5, 7, 11, 13}

var s []int = primes[1:4]
fmt.Println(s)
}

切片数组,可以改变指向的范围

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
r := []bool{true, true, true, false, false, false}
t := []bool{true, true, true, false, false, false}
fmt.Println(r)
r = r[1:2]
fmt.Println(r)
r = r[0:3]
fmt.Println(r)
# 输出[true true false],即最开始的[1,4]
r = t
fmt.Println(r)
}

切片,可省略下界或上界

cap() 查看容量,即从下界到数组最后一个元素的个数

len()查看长度,即从下界到上界的个数

切片为nil时,caplen都为0

使用make来创建一维切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
a := make([]int, 5)
printSlice("a", a)

b := make([]int, 0, 5) // len(b)=0, cap(b)=5
printSlice("b", b)

c := b[:2]
printSlice("c", c)

d := c[2:5]
printSlice("d", d)
}

func printSlice(s string, x []int) {
fmt.Printf("%s len=%d cap=%d %v\n",
s, len(x), cap(x), x)
}

使用make创建二维切片,第一种方式,该方式允许第二维大小有所变化:

1
2
3
4
5
6
// 创建一个位数为[dx][dy]的切片
a := make([][]uint8, dx)
for i := range a {
a[i] = make([]uint8, dy)
}

第二种方式,不允许第二维大小有所变化,因为改变了就会有可能覆盖:

1
2
3
4
5
6
7
8
9
// Allocate the top-level slice, the same as before.
picture := make([][]uint8, YSize) // One row per unit of y.
// Allocate one large slice to hold all the pixels.
pixels := make([]uint8, XSize*YSize) // Has type []uint8 even though picture is [][]uint8.
// Loop over the rows, slicing each row from the front of the remaining pixels slice.
for i := range picture {
picture[i], pixels = pixels[:XSize], pixels[XSize:]
}

可使用append函数向切片添加元素,假如切片容量不足,则容量会翻倍。

1
2
3
4
5
6
func main() {
var s []int
s = append(s, 2, 3, 4)
fmt.Println(s)
}

还可以使用append函数向切片中添加切片:

1
2
3
4
5
x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)
fmt.Println(x)

可使用range遍历切片,每次循环会有两个值,一个是元素的下标,一个是元素的值。可使用_忽略其中一个。

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
pow := []int{4, 1, 5}
for _, value := range pow {
fmt.Printf("%d\n", value)
}

// 只有一个值只会得到下标
for idx := range pow {
fmt.Printf("%d\n", pow[idx])
}
}

缩减切片的大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var digitRegexp = regexp.MustCompile("[0-9]+")

// ugly
func FindDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
return digitRegexp.Find(b)
}

// elegant,缩减了切片的cap,释放内存
func CopyDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
b = digitRegexp.Find(b)
c := make([]byte, len(b))
copy(c, b)
return c
}

Maps

maps的零值为nil,可通过make创建map

1
2
3
4
5
6
7
8
9
10
11
type Vertex struct {
Lat, Long float64
}
func main() {
m := make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"])
}

初始化

1
2
3
4
5
6
7
8
9
var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}

初始化时,值的类型名可省略

1
2
3
4
5
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}

插入键值对,取键的值,删除键值对(可多次删除,可删除不存在的键)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
m := make(map[string]int)

m["Answer"] = 42
fmt.Println("The value:", m["Answer"])

m["Answer"] = 48
fmt.Println("The value:", m["Answer"])

delete(m, "Answer")
fmt.Println("The value:", m["Answer"])

v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)
}

map被函数调用,可被修改。

Function values

函数也可以作为值,可以像其他数据类型一样赋值给变量,作为实参等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}

func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(5, 12))

fmt.Println(compute(hypot))
fmt.Println(compute(math.Pow))
}

Function closures

函数闭包,不同变量可以绑定不同的函数闭包,相互之间不影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}

func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}

Flow control statements

if

if,可以没有(),但一定要有{}

1
2
3
4
if x < 0 {
return sqrt(-x) + "i"
}

if可以像for那样先带个statement。假如申明了变量,则只能在if或后续的else中使用。

1
2
3
4
5
6
7
8
9
10
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
// can't use v here, though
return lim
}

for

for,可以没有(),但一定要有{}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for i := 0; i < 10; i++ {
sum += i
}

// 相当于while(condition)
i := 0
for i < 10 {
DoSomething();
i++;
}

// 相当于while(true)
for {
DoSomething();
}

switch

switch, 满足其中一个case即执行其中的语句,不会再执行其他case的语句,可看成自带break。此外,case不一定要接整数常量,它可以不是整数,可以不是常量。但要注意case后接的类型和switch比较的类型相同。

switch同样可以像for那样先带个statement,且default的位置可放在首位,因为它总是会在所有条件都不匹配的时候才执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
}
}

switch还可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}

defer

defer 推迟执行,具有LIFO的性质

1
2
3
4
5
6
7
func main() {
defer fmt.Printf("1 ")
defer fmt.Printf("2 ")
fmt.Printf("3 ")
// 将输出3 2 1
}

defer还可以这样写,参数部分的函数是会先执行的:

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
func trace(s string) string {
fmt.Println("entering:", s)
return s
}

func un(s string) {
fmt.Println("leaving:", s)
}

func a() {
defer un(trace("a"))
fmt.Println("in a")
}

func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}

func main() {
b()
}
/* 输出:
entering: b
in b
entering: a
in a
leaving: a
leaving: b
*/

Methods and interfaces

Methods

Go没有class,但可以给方法(函数)指定适用的类型。method是指定类型的function

1
2
3
4
5
6
7
8
9
10
11
12
13
type Vertex struct {
X, Y float64
}

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

func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}

methord指定的类型可以是基本类型,但所指定的类型必须在本package出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type MyFloat float64

func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}

func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}

methord指定的类型可以是指针,则我们可以修改指针指向的内容,并且不用产生拷贝开销。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}

func main() {
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs())
}

Interfaces

接口,要求使用接口的类型实现了接口中方法

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
type Abser interface {
Abs() float64
}

func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}

a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser

// In the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
// a = v

fmt.Println(a.Abs())
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}

type Vertex struct {
X, Y float64
}

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

实现接口中的方法不需要像其他语言那样使用显式的关键字,如implement

这样做可以让申明和实现分离,不需要特殊处理就可以让他们放在不同的包中。

接口可以看成是一个二元组(value, type),对于一个value,它会调用接收了type的方法。

假如一个变量是接口类型的,那么它有可能valuetype都为nil,这种情况下会RE。而当type不为nil时,它是非空的,但是value可能会空,因此我们需要在实现接口的方法里处理好这种情况。

空接口,用于存储任何类型的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
var i interface{}
describe(i)

i = 42
describe(i)

i = "hello"
describe(i)
}

func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}

类型断言,在断言不成立的时候应该用两个变量存储结果,否则会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
var i interface{} = "hello"

s := i.(string)
fmt.Println(s)

s, ok := i.(string)
fmt.Println(s, ok)

f, ok := i.(float64)
fmt.Println(f, ok)

f = i.(float64) // panic
fmt.Println(f)
}

type switch 可以依次进行多个类型断言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)
}

Stringer 用于输出自定义类型的接口

1
2
3
4
type Stringer interface {
String() string
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type Person struct {
Name string
Age int
}

func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}

Error 用于输出错误信息的接口

1
2
3
4
type error interface {
Error() string
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type MyError struct {
When time.Time
What string
}

func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}

func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}

func main() {
if err := run(); err != nil {
fmt.Println(err)
}
}

Reader 用于读取数据的接口

func (T) Read(b []byte) (n int, err error)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
r := strings.NewReader("Hello, Reader!")

b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}

Images 用于处理图像的接口

1
2
3
4
5
6
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}

Concurrency

Goroutines 轻型线程,它们共享同一地址的内存,需要同步控制

go f(x, y, z) 创建一个新Goroutine运行函数f

Channels 可用于传递数据的一种数据类型,需要用到运算符<-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}

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)
}

Buffered Channels 可理解为大小的Channel,满了还往里面添加的话会报错

可使用range来取出channel中的所有数据,注意channel要close

<-ch实际上会返回两个值,第二个值代表是否还有数据,即false 表示channel close了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}

select 用于多个channel的选择,哪个channel有数据就执行哪一个,假如同时有数据来了,就随机先执行其中一个

select中的default在没有收到任何channel数据的时候执行

sync.Mutex 用于互斥

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
import (
"fmt"
"sync"
"time"
)

// SafeCounter is safe to use concurrently.
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}

// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key]++
c.mux.Unlock()
}

// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mux.Unlock()
return c.v[key]
}

func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}

time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}

Diagnostics | 诊断

pprof

1
2
3
4
5
// 起手式
cpuProfile, _ := os.Create("cpu_profile")
pprof.StartCPUProfile(cpuProfile)
defer pprof.StopCPUProfile()

go tool pprof <file>

web 启动可视化界面

top 列出cpu占比最高的函数

list <function> 列出指定函数的数据

Go Command

go build

假如想要玩一下go build命令,注意加上-a重新全部编译,否则会因为有缓存而跳过编译过程。

假如是main,则会编译生成可执行文件。

否则,只会编译,不产生文件,用于验证代码能否编译。

go install

假如是main,则会在$GOPATH/bin下生成可执行文件。

否则,会在$GOPATH/pkg下生成*.a文件。

go get

1
2
# 升级包
go get -u <package>

go clean

删除编译生成的文件等等。

依赖管理工具

Dep

1
2
3
4
5
6
7
8
9
10
11
# 初始化,-v参数代表输出详细信息
dep init -v

# 解决依赖的BUG
dep ensure -v

# 更新依赖,假如总是更新失败,先dep ensure -v一下,并选择在网络空闲的时候更新
dep ensure -update -v

# 添加一条依赖
dep ensure -add github.com/bitly/go-simplejson

Go Mod

拉取速度比Dep要快很多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 设置环境变量
export GO111MODULE=on

# 初始化,生成go.mod文件
go mod init

# 之后即可使用go build或go test拉取依赖
go build/test

# 升级所有依赖到最新
go get -u

# 将依赖放在项目的vendor中
go mod vendor

假如遇到某个依赖有问题,更把那个依赖删掉,然后再go get -u升级所有依赖。