注释
在编码的阶段同时写好变量、函数、包等的注释.
注释必须是完整的句子, 以需要注释的内容作为开头, 句点作为结尾.
程序中每一个被导出的(大写的)名字, 都应该有一个文档注释.
包注释
每个程序包都应该有一个包注释, 一个位于 package 子句之前的块注释或行注释.
包如果有多个 go 文件, 包注释只需要出现在一个 go 文件中即可.
1 | // Package regexp implements a simple library for regular expressions. |
可导出类型
第一条语句应该为一条概括语句, 并且使用被声明的名字作为开头.
1 | // Compile parses a regular expression and returns, if successful, a Regexp object |
命名
使用短命名, 长名字并不会自动使得事物更易读, 文档注释会比格外长的名字更有用.
包名
包名应该为小写单词, 不要使用下划线或者混合大小写.
接口名
单个函数的接口名以 “er” 作为后缀, 如 Reader, Writer.
接口的实现则去掉 “er”.
1 | type Reader interface { |
两个函数的接口名, 综合两个函数名:
1 | type WriteFlusher interface { |
而三个以上函数的接口名, 类似于结构体名:
1 | type Car interface { |
混合大小写
采用驼峰式命名
MixedCaps 大写开头, 可导出
mixedCaps 小写开头, 不可导出
函数
函数采用命名的多值返回
传入变量和返回变量以小写字母开头
1 | func nextInt(b []byte, pos int) (value, nextPos, int) { |
控制结构
if
if 接受初始化语句, 约定如下方式建立局部变量:
1 | if err := file.Chmod(0644); err != nil { |
for
采用短声明建立局部变量:
1 | sum := 0 |
range
如果只需要第一项(key), 就丢弃第二个:
1 | for key := range m { |
如果只需要第二项, 则把第一项置为下划线:
1 | sum := 0 |
return
尽早 return. 一旦有错误发生, 马上返回.
1 | f, err := os.Open(name) |
错误处理
error 作为函数的值返回, 必须对 error 进行处理.
错误描述如果是英文, 必须为小写, 并且不需要标点. 结尾采用独立的错误流进行处理.
不要像下面这样做:
1 | if err != nil { |
而应该采用以下的方式:
1 | if err != nil { |
如果返回值需要初始化, 则采用下面的方式:
1 | x, err := f() |
panic
尽量不要使用 panic, 除非你知道自己在做什么.
import
对 import 的包进行分组管理, 而且遵循 标准库
、第三方库
、自库
的分组顺序.
1 | package main |
缩写
采用全部大写, 或者全部小写来表示缩写单词.
比如对于 url 这个单词, 不要使用 UrlPony
, 而要使用 urlPony
或者 URLPony
.
参数传递
- 对于少量数据, 不要传递指针.
- 对于大量数据的
struct
, 可以考虑使用指针. - 传入参数是
map
、slice
、chan
, 不要传递指针(因为这3种为引用类型).
类型
接受者名称, 统一采用单字母 p
而不是 this
, me
或者 this
:
1 | type T struct {} |
接受者的类型如果不清楚, 统一采用指针型:
1 | func (p *T) Get() {} |
而不是:
1 | func (p T) Get() {} |
在某些情况下, 出于性能的考虑, 或者类型本来就是引用类型, 存在以下的特例情况:
- 如果接收者是
map
、slice
、chan
, 不要用指针传递:
1 | // Map |
1 | // Channel |
- 如果需要对
slice
进行修改, 通过返回值的方式重新赋值:
1 | // slice |
- 如果接收者是含有
sync.Mutex
或者类似同步字段的结构体, 必须使用指针传递, 以避免复制:
1 | package main |
- 如果接收者是大的结构体或者数组, 使用指针传递会更有效率:
1 | package main |