实践Go

go中国

安装

下载解压,设置环境变量(GOROOT)即可完成安装。

1
2
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin

设置go工作目录(GOPATH),也可以不用设置,默认工作目录为~/go

1
2
3
export GOPATH=~/GoProjects
# 将bin目录加入到PATH
PATH="$GOPATH/bin:$PATH"

工作目录结构

1
2
3
4
5
GoProjects$ tree -L 1
.
├── bin
├── pkg
└── src

src存放源代码,目录即为包(跟Java类似),pkg存放go编译生成的文件,bin存放安装后的可执行文件

代理

1
2
$ export GO111MODULE=auto
$ export GOPROXY=https://goproxy.cn

欢迎访问阿里云Go Module代理仓库服务

Goproxy.cn

快速开始

新建hello项目,项目结构如下:

1
2
3
4
5
6
7
8
9
GoProjects$ tree
.
├── bin
├── pkg
└── src
    └── ray.wang
        └── hello
            └── main.go

main.go

1
2
3
4
5
6
7
8
9
package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello World")
}

运行

1
$ go run main.go

安装

1
2
3
4
5
# ray.wang/hello为包,生成hello文件存放于bin目录
$ go install ray.wang/hello

$ hello
Hello World

跨平台编译

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
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/ray/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/ray/GoProjects"
GOPROXY=""
GORACE=""
GOROOT="/home/ray/platform/go"
GOTMPDIR=""
GOTOOLDIR="/home/ray/platform/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build887930678=/tmp/go-build -gno-record-gcc-switches"

注意里面两个重要的环境变量GOOSGOARCH,其中GOOS指的是目标操作系统,它的可用值为:

  1. darwin
  2. freebsd
  3. linux
  4. windows
  5. android
  6. dragonfly
  7. netbsd
  8. openbsd
  9. plan9
  10. solaris

一共支持10中操作系统。GOARCH指的是目标处理器的架构,目前支持的有:

  1. arm
  2. arm64
  3. 386
  4. amd64
  5. ppc64
  6. ppc64le
  7. mips64
  8. mips64le
  9. s390x
1
$ GOOS=windows GOARCH=amd64 go build ray.wang/hello

安装远程包

go提供了一个获取远程包的工具go get,他需要一个完整的包名作为参数,只要这个完成的包名是可访问的,就可以被获取到,go get的本质是使用源代码控制工具下载这些库的源代码,比如git,hg等。比如:

1
$ go get -v github.com/spf13/cobra/cobra

更新远程包

1
$ go get -u -v github.com/spf13/cobra/cobra

备用方案

go get其实执行了git clonego install命令,当无法执行go get时(比如:golang.org访问不了)可以手动处理。

git clone

1
$ git clone git@github.com:cweill/gotests.git

go install

1
$ go install github.com/cweill/gotests/gotests

获取Gitlab私有包

设置私服地址http到git转换

1
$ git config --global url."git@git.flysnow.org:".insteadOf "http://git.flysnow.org/"
1
$ go get -v -insecure git.flysnow.org/hello

基础

变量

1
2
3
4
5
6
7
8
9
10
// 未初始化的变量的值为零值(zero value)
var 变量名[,变量名] 类型
// type inference
var 变量名[,变量名] [类型] = 表达式[,表达式]
变量名[,变量名] := 表达式[,表达式]
// 多个变量的类型不同
var (
    变量名[,变量名] [类型1] = 表达式[,表达式]
    变量名[,变量名] [类型2] = 表达式[,表达式]
)

类型

bool:true或false

数字类型

int和uint是整型的统称,根据操作系统的位数决定是32位还是64位

  • int8, int16, int32, int64, int

  • uint8, uint16, uint32, uint64, uint

  • float32, float64(默认)

  • complex64, complex128

    1
    2
    c1 := complex(5, 7)
    c2 := 8 + 27i
    
  • byte

    an alias for uint8

  • rune

    an alias for int32, Unicode code points

    1
    2
    3
    4
    5
    6
    7
    8
    9
    s:="abc"
    runes := []rune(s)
    for i:= 0; i < len(runes); i++ {
    	fmt.Printf("%c ",runes[i])
    }
      
    // Constructing a string from slice of runes
    runeSlice := []rune{0x0053, 0x0065, 0x00f1, 0x006f, 0x0072}
    str := string(runeSlice)
    

string

byte slice

1
2
3
4
5
6
7
8
s:="abc"
for i:= 0; i < len(s); i++ {
	fmt.Printf("%x ", s[i])
}

// Constructing string from slice of bytes
byteSlice := []byte{67, 97, 102, 195, 169}//decimal equivalent of {'\x43', '\x61', '\x66', '\xC3', '\xA9'}
str := string(byteSlice)

类型转换

go对于变量的类型有严格的限制,不同类型之间的变量不能进行赋值、表达式等操作,必须要要转换成同一类型才可以。把 v 转换为 T 类型的语法是 T(v)

常量

1
2
// 省略类型则是无类型的常量
const 变量名 [类型] = 表达式

无类型的常量有一个与它们相关联的默认类型,并且当且仅当一行代码需要时才提供它。常量可以赋值给 “合适的” 类型,而不需要类型转换。

1
2
package main
package abc
  • 以包为单位组织模块
  • 一个目录(不包含子目录)代表一个包
  • 一个目录(不包含子目录)下的文件的包名必须相同
  • 包名一般取目录名,也可以跟目录名不同
  • 引用时取全路径目录名
  • main是一个特殊的包名,它表示当前是一个可执行程序,而不是一个库
1
2
3
4
5
import "fmt"
import (
	"github.com/spf13/hugo/commands"
	"io"
)

引用包但是不使用包会报错,可以使用空白标识符,会执行包的初始化

1
2
3
4
5
import (  
    "geometry/rectangle" 
)

var _ = rectangle.Area // 错误屏蔽器

1
2
3
import (
    _ "geometry/rectangle" 
)

函数

1
2
3
func functionname(parametername type) returntype {  
    // 函数体(具体实现的功能)
}

init函数

包可以包含一个init函数,不包含参数和返回值,用于包的初始化,只会被初始化一次,不能显示调用

1
2
func init() {  
}

main函数

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
        fmt.Print("Hello, World!")
}

可变参数函数

1
2
3
4
5
6
7
8
9
func functionName(..., elems ...Type) returnType

//调用
functionName(1,2)
functionName(1,2,3,4)

//用切片调用
a:=[]{1,2,3}
functionName(a...)

大小写标记访问权限

大些字母开头的变量或者函数等是public的,可以被其他包访问;小些的则是private的,不能被其他包访问到

数组

value type

1
2
3
4
var 变量名 [number]类型
var 变量名 = [number]类型{value0,value1...}]
变量名 := [number]类型{value0,value1...}
变量名 := [...]类型{value0,value1...}

切面(Slice)

reference type

1
2
3
4
5
6
7
8
9
10
11
12
a := [5]int{76, 77, 78, 79, 80}
//creates a slice from a[1] to a[3]
var b []int = a[1:4] 

//creates an array and returns a slice reference
c := []int{76, 77, 78}

// creating a slice using make
i := make([]int, 5, 5)

// appending to a slice
cars = append(cars, "Toyota")

Map

reference type

1
2
3
4
5
6
7
8
9
personSalary := make(map[string]int) 

personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }

personSalary["mike"] = 9000
delete(personSalary, "steve")

循环

for range loop

1
2
3
4
s:="abc"
for index, rune := range s {
	fmt.Printf("%c starts at byte %d\n", rune, index)
}

指针

declaring pointers

1
2
b := 255
var a *int = &b

zero value

1
2
3
4
5
6
7
a := 25
var b *int
if b == nil {
	fmt.Println("b is", b)
	b = &a
	fmt.Println("b after initialization is", b)
}

new function

1
2
3
4
5
6
func main() {  
    size := new(int)
    fmt.Printf("Size value is %d, type is %T, address is %v\n", *size, size, size)
    *size = 85
    fmt.Println("New size value is", *size)
}

Dereferencing a pointer

1
2
3
4
5
6
func main() {  
    b := 255
    a := &b
    fmt.Println("address of b is", a)
    fmt.Println("value of b is", *a)
}

struct

value type

declaring a structure

1
2
3
4
5
// named structure
type Employee struct {  
    firstName, lastName string
    age, salary         int
}
1
2
3
4
5
// anonymous structures
var employee struct {  
        firstName, lastName string
        age int
}

creating a structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// named structure, declare at first
type Employee struct {  
    firstName, lastName string
    age, salary         int
}

func main() {
    //creating structure using field names
    emp1 := Employee{
        firstName: "Sam",
        age:       25,
        salary:    500,
        lastName:  "Anderson",
    }

    //creating structure without using field names
    emp2 := Employee{"Thomas", "Paul", 29, 800}

    fmt.Println("Employee 1", emp1)
    fmt.Println("Employee 2", emp2)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// creating anonymous structures
func main() {  
    emp3 := struct {
        firstName, lastName string
        age, salary         int
    }{
        firstName: "Andreah",
        lastName:  "Nikola",
        age:       31,
        salary:    5000,
    }

    fmt.Println("Employee 3", emp3)
}

zero value

accessing fields

1
2
3
4
5
6
7
8
9
10
11
12
13
type Employee struct {  
    firstName, lastName string
    age, salary         int
}

func main() {  
    emp6 := Employee{"Sam", "Anderson", 55, 6000}
    fmt.Println("First Name:", emp6.firstName)
    fmt.Println("Last Name:", emp6.lastName)
    emp6.age=60
    fmt.Println("Age:", emp6.age)
    fmt.Printf("Salary: $%d", emp6.salary)
}

pointers to structure

1
2
3
4
5
6
7
8
9
10
11
12
13
type Employee struct {  
    firstName, lastName string
    age, salary         int
}

func main() {  
    emp8 := &Employee{"Sam", "Anderson", 55, 6000}
    fmt.Println("First Name:", (*emp8).firstName)
    fmt.Println("Age:", (*emp8).age)
    // emp8.firstName instead of the explicit dereference (*emp8).firstName to access the firstName field
    fmt.Println("First Name:", emp8.firstName)
    fmt.Println("Age:", emp8.age)
}

anonymous fields

1
2
3
4
5
6
7
8
9
type Person struct {  
    string
    int
}

func main() {  
    p := Person{"Naveen", 50}
    fmt.Println(p)
}

Nested structs

方法

方法其实就是一个函数,在 func 这个关键字和方法名中间加入了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型。接收器是可以在方法的内部访问的。必须与Type定义在同一个包。

1
2
func (t Type) methodName(parameter list) {
}

接口

empty interface

type assertion

1
2
3
4
5
6
7
8
9
func assert(i interface{}) {  
    s := i.(int) 
    fmt.Println(s)
}

func assert(i interface{}) {  
    v, ok := i.(int)
    fmt.Println(v, ok)
}

type switch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func findType(i interface{}) {  
    switch i.(type) {
    case string:
        fmt.Printf("I am a string and my value is %s\n", i.(string))
    case int:
        fmt.Printf("I am an int and my value is %d\n", i.(int))
    default:
        fmt.Printf("Unknown type\n")
    }
}

func findType(i interface{}) {  
    switch v := i.(type) {
    case Describer:
        v.Describe()
    default:
        fmt.Printf("unknown type\n")
    }
}

并发(concurrency)

通道(channels)

1
2
3
4
5
6
7
8
func main() {  
    var a chan int
    if a == nil {
        fmt.Println("channel a is nil, going to define it")
        a = make(chan int)
        fmt.Printf("Type of a is %T", a)
    }
}
1
a := make(chan int)  
1
2
data := <- a // read from channel a  
a <- data // write to channel a  
1
2
3
4
5
6
7
8
9
10
func hello(done chan bool) {  
    fmt.Println("Hello world goroutine")
    done <- true
}
func main() {  
    done := make(chan bool)
    go hello(done)
    <-done
    fmt.Println("main function")
}

依赖管理

dep,govendor,glide,godep

vgo

参考

Go语言中文网(Golang中文社区)

Go 系列教程Golang tutorial series

Go语言环境搭建详解

从Java到Golang快速入门