原文地址
开发环境搭建
golang 的开发环境搭建比较简单,由于是编译型语言,写好 golang 源码后,只需要执行 go build
就能将源码编译成对应平台(本文中默认为 linux)上的可执行程序。本文不再赘述如何搭建 golang 开发环境,只说明下需要注意的地方。
从官网下载对应平台的 golang 安装包中包括 golang 的编译器、一些工具程序和标准库源码。早期的 golang 版本中,需要设置 GOROOT
和 GOPATH
两个环境变量。
从 1.8 版开始,GOPATH
不再需要显示设置。如果没有显示设置,则 GOPATH
的默认值为 $HOME/go
。GOPATH
可以设置多个目录,但推荐只设置一个或直接使用默认值,多个 GOPATH
会造成依赖管理的困难。推荐将 $GOPATH/bin
加到 $PATH
里,这样通过 go install
会安装到 $GOPATH/bin
目录的可执行程序可以像系统命令一样直接运行,不用输入完整路径。
从 1.10 版开始, GOROOT
也不再需要显示设置了,只需要将安装包中的 bin 目录加到 $PATH
里,系统会自动推导出 GOROOT
的值。
编辑器根据个人喜好选择,作者主要使用 vim 和 vscode 。这里介绍了使用 vim 时需要安装的插件(安装过程可能需要翻墙,YCM 安装比较复杂可以不要,gocode 够用了)。
hello world
以下是 golang 版本的 hello world:
package main
import (
"fmt"
)
func main() {
fmt.Println("hello world")
}
golang 安装包自带的 gofmt 能将源码格式化成官方推荐的风格,建议将这个工具整合到编辑器里。
这个简单的程序用 go build 编译出来可执行程序用 ldd 查看发现没有任何动态库依赖,size 也比较大(1.8M ,对等的 C 程序版本只有 7.5K)。实际上这里也体现了 golang 的哲学:直接通过源代码分发软件,所有的代码编到一整个可执行程序里,基本没有动态库依赖(或者只依赖 C/C++ 运行时库和基本的系统库),这也方便了 docker 化(C/C++ 程序员应试能体会动态库依赖有多恶心)。通过 readelf 查看可执行程序会发现代码段和调试信息段占用了比较大的空间,代码段大是因为 golang 的运行时也在里面。调试信息段方便 golang 进程 panic 时会打印详细的进程堆栈及源码信息,这也是为什么 golang 的可执行程序比较大的原因。
命名规范
golang 的标准库提供了 golang 程序命名规范很好的参考标准,命名规范应该尽量和标准库的风格接近,多看下标准库的代码就能体会到 golang 的命名哲学了。
命名在很大程序上也体现了一名程序员的修养,用好的命名写出的代码通常是自注释的,只需要在有复杂的逻辑需要解释的情况下才额外注释。
好的命名应该具有以下特征:
- 一致性:见名知义,比如标准库中将对象序列化成字符串的操作名为
String
,在你自己的代码里将自定义类型的对象序列化成字符串也应该叫这个名字,并且签名和标准库要一致; - 简明精炼:减少敲键盘的次数;
- 精确性:不要使用有歧义的命名。
通常变量的作用域越广,变量的名字应该越长,反之亦然。
golang 中一般使用驼峰命名法,尽量不要使用下划线(基本只在全大写的常量命名中使用)。首字母缩略词应该全部大写,比如 ServeHTTP
, IDProcessor
。
本文中出现的必须、 禁止是指强烈推荐的 golang 风格的规范,但违反这个规范并不会导致程序编译不过。
常量
全大写或者驼峰命名都可以,全大写的情况下可使用下划线分隔单词:
const (
SEEK_SET int = 0 // seek relative to the origin of the file
SEEK_CUR int = 1 // seek relative to the current offset
SEEK_END int = 2 // seek relative to the end
)
const (
MaxInt8 = 1<<7 - 1
MinInt8 = -1 << 7
MaxInt16 = 1<<15 - 1
MinInt16 = -1 << 15
MaxInt32 = 1<<31 - 1
MinInt32 = -1 << 31
MaxInt64 = 1<<63 - 1
MinInt64 = -1 << 63
MaxUint8 = 1<<8 - 1
MaxUint16 = 1<<16 - 1
MaxUint32 = 1<<32 - 1
MaxUint64 = 1<<64 - 1
)
局部变量
通过以下代码片断举例说明局部变量的命名原则:
func RuneCount(buffer []byte) int {
runeCount := 0
for index := 0; index < len(buffer); {
if buffer[index] < RuneSelf {