程序人生 A log of my life

go语言

golang是当下最火的语言了,以1.9版本为例进行记录。

安装

安装下载的exe安装包,就自动安装好go语言了。

go语言自带包管理器,所以下一步很重要,设置一个GOPATH环境变量,作为所有package的根,在go 1.8之后,这个变量有缺省值了,是%userdir%/go,但是通常还是设置一下比较好,比如:

GOPATH=d:\dev\go

理论上,GOPATH可以设置为多个目录,用冒号(Linux)或分号(windows)分隔,但是一些工具,并以不能很好的支持多PATH,所以单个PATH是最保险的做法。

调试器

设置好GOPATH后,新启动一个命令行窗口,输入

go get github.com/derekparker/delve/cmd/dlv

安装go调试器,这是比较令人困惑的一点,为什么没有内置在语言的安装包里。

IDE

支持Go的当前最好的IDE应该是VSCode和Jetbrain的Gogland,以VSCode为例,直接安装运行, 然后点击扩展安装lukehoban的go插件,重启VSCode,这就好了,然后还有一点操作和设置记录一下:

  • 用打开目录的方式打开GOPATH这个目录
  • 新建文件,输入go代码,VSCode会提示安装一些go package,点击安装全部(关于代理,参考下面一节)
  • 语法提示和调试应该都已经好了。
  • 语法和编译错误,需要在保存之后调用go build,根据结果来提示错误,好在速度还比较快,如果很慢,可能以为在save的时候做了一些多余的操作,比如go vet,可以通过配置关闭这些多余的save操作。

hello,world

打开GOPATH目录,新建一个hello.go

package main
import "fmt"

func main() {
	fmt.Println("hello,world")
}

在vscode的terminal下输入go run hello.go即可运行。想要调试的话,可以直接在代码行之前设置断点,点击工具栏上的debug按钮。

代理

众所周知的原因,某些库无法下载,需要先做以下设置(当然你得有这个8888的proxy)

set http_proxy=socks5://127.0.0.1:8888
set https_proxy=socks5://127.0.0.1:8888

在这种情况下,VSCdode中的提示,就不要直接安装,而是复制到命令行下通过代理就安装,当然如果你能设置好VSCode的代理,也就直接可以在VSCode中安装了。

包管理

Go自带了一个包管理,go get,但是没有版本管理,也就是存在一个基本的问题,就是所有的包依赖都没有版本,如果依赖包更新了,很可能导致编译失败,go官方认为版本管理比较复杂,所有把这个复杂问题回避了,并给出一个规避方法就是把依赖放入自己项目中。第三方有一个gopkg.in,可以做部分的版本管理功能(在包名上加版本号)。

Go的import命令比较有趣,每次import一个package(目录),而不像Java是一个Class(文件),import后面给的是path,而不是包名,但是import后使用的变量却是包名,因为大部分情况下,path的最后一段就是包名,所以感受不到区别,但这个只是约定,不是强制。因为每个目录下只允许一个package,所以可以import 的时候可以用一个别名,就替代了引入的那个包名。

发布

一般情况下,使用 go build 就可以做发布了,一个包括fmt包的hello,world的大小约为1.8M,如果要做缩小,可以加参数

go build  -ldflags "-s -w"

这样可以省不少空间,大概从1.8M,降低到1.2M,但这不是一个常见做法,可能会让某些功能(比如反射)失效。

实际上这么大的主要原因在于fmt包(加上依赖)比较大,如果不使用fmt,大概可以降低到700k左右,这基本上是go的runtime的大小了。

Go工具链

Go工具链是一致受到推崇的,主要包括format、lint、vet等。

说说go

以Java背景来看,除了稍怪异的语法,还有一些地方Go和Java有明显不同,记录一下:

  • 数组在Go里有Array和Slice两个类型,Array是传值(这个比较奇怪),Slice才是引用,所以Slice相当于就是Java里的List了。
  • Go的Map基本和Java比较接近
  • Go支持指针,类似C语言,这在Java里没有,Go的指针不支持加减运算,也不需要用->访问元素,相对简单一些。
  • Go的struct,结合method,部分具有class的封装功能,但是method的this缺省是传值的,需要声明为指针才能传引用。
  • Go的defer,虽然语义上有些类似Java的finally,但是

Go的nil和Java的null虽然类似,但很多地方似乎设计的很不一致:

  • struct, array的初值不是nil,而是每个元素都为缺省值
  • slice的初值是nil,但是可以取len,为0
  • 指针的初值为nil

轮子

记录一些好用或有趣的轮子:

  • cobra A Commander for modern Go CLI interactions
  • chi lightweight, idiomatic and composable router for building Go HTTP services