一、导入标准库、第三方或其它包

除标准库外,Go 语言的导入路径基本上依赖代码托管平台上的 URL 路径,因此一个源文件需要导入的包有 4 种分类:标准库、第三方包、组织内其它包和当前包的子包。

基本规则:

  • 如果同时存在 2 种及以上,则需要使用分区来导入。每个分类使用一个分区,采用空行作为分区之间的分割。
  • 在非测试文件(*_test.go)中,禁止使用 . 来简化导入包的对象调用。
  • 禁止使用相对路径导入(./subpackage),所有导入路径必须符合 go get 标准。

下面是一个完整的示例:

import ("fmt""html/template""net/http""os""github.com/codegangsta/cli""gopkg.in/macaron.v1""github.com/gogits/git""github.com/gogits/gfm""github.com/gogits/gogs/routers""github.com/gogits/gogs/routers/repo""github.com/gogits/gogs/routers/user"
)

注释规范

  • 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。
  • 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。
  • 包、函数、方法和类型的注释说明都是一个完整的句子。
  • 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。
  • 注释的单行长度不能超过 80 个字符。

包级别

  • 包级别的注释就是对包的介绍,只需在同个包的任一源文件中说明即可有效。
  • 对于 main 包,一般只有一行简短的注释用以说明包的用途,且以项目名称开头:
 // Gogs (Go Git Service) is a painless self-hosted Git Service.package main
  • 对于一个复杂项目的子包,一般情况下不需要包级别注释,除非是代表某个特定功能的模块。

  • 对于简单的非 main 包,也可用一行注释概括。

  • 对于相对功能复杂的非 main 包,一般都会增加一些使用示例或基本说明,且以 Package 开头:

  /*Package regexp implements a simple library for regular expressions.The syntax of the regular expressions accepted is:regexp:concatenation { '|' concatenation }concatenation:{ closure }closure:term [ '*' | '+' | '?' ]term:'^''$''.'character'[' [ '^' ] character-ranges ']''(' regexp ')'*/package regexp
  • 特别复杂的包说明,可单独创建 doc.go 文件来加以说明。

结构、接口及其它类型

  • 类型的定义一般都以单数形式描述:
  // Request represents a request to run a command.type Request struct { ...
- 如果为接口,则一般以以下形式描述:// FileInfo is the interface that describes a file and is returned by Stat and Lstat.type FileInfo interface { ...

函数与方法

  • 函数与方法的注释需以函数或方法的名称作为开头:
  // Post returns *BeegoHttpRequest with POST method.
  • 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述:
 // Copy copies file from source to target path.// It returns false and error when error occurs in underlying function calls.
  • 若函数或方法为判断类型(返回值主要为 bool 类型),则以 returns true if 开头:
  // HasPrefix returns true if name has any string in given slice as prefix.func HasPrefix(name string, prefixes []string) bool { ..

.

其它说明

  • 当某个部分等待完成时,可用 TODO: 开头的注释来提醒维护人员。
  • 当某个部分存在已知问题进行需要修复或改进时,可用 FIXME: 开头的注释来提醒维护人员。
  • 当需要特别说明某个问题时,可用 NOTE: 开头的注释:
 // NOTE: os.Chmod and os.Chtimes don't recognize symbolic link,// which will lead "no such file or directory" error.return os.Symlink(target, dest)

二、命名规则

文件名

  • 整个应用或包的主入口文件应当是 main.go 或与应用名称简写相同。例如:Gogs 的主入口文件名为 gogs.go。

函数或方法

若函数或方法为判断类型(返回值主要为 bool 类型),则名称应以 Has, Is, Can 或 Allow 等判断性动词开头:

  func HasPrefix(name string, prefixes []string) bool { ... }func IsEntry(name string, entries []string) bool { ... }func CanManage(name string) bool { ... }func AllowGitHook() bool { ... }

常量

  • 常量均需使用全部大写字母组成,并使用下划线分词:
  const APP_VER = "0.7.0.1110 Beta"
  • 如果是枚举类型的常量,需要先创建相应类型:
  type Scheme stringconst (HTTP  Scheme = "http"HTTPS Scheme = "https")
  • 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀:
  type PullRequestStatus intconst (PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iotaPULL_REQUEST_STATUS_CHECKINGPULL_REQUEST_STATUS_MERGEABLE)

变量

  • 变量命名基本上遵循相应的英文表达或简写。
  • 在相对简单的环境(对象数量少、针对性强)中,可以将一些名称由完整单词简写为单个字母,例如:
user 可以简写为 u
userID 可以简写 uid
  • 若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头:
  var isExist boolvar hasConflict boolvar canManage boolvar allowGitHook bool
  • 上条规则也适用于结构定义:
  // Webhook represents a web hook object.type Webhook struct {ID           int64 `xorm:"pk autoincr"`RepoID       int64OrgID        int64URL          string `xorm:"url TEXT"`ContentType  HookContentTypeSecret       string `xorm:"TEXT"`Events       string `xorm:"TEXT"`*HookEvent   `xorm:"-"`IsSSL        bool `xorm:"is_ssl"`IsActive     boolHookTaskType HookTaskTypeMeta         string     `xorm:"TEXT"` // store hook-specific attributesLastStatus   HookStatus // Last delivery statusCreated      time.Time  `xorm:"CREATED"`Updated      time.Time  `xorm:"UPDATED"`}

变量命名惯例

变量名称一般遵循驼峰法,但遇到特有名词时,需要遵循以下规则:

  • 如果变量为私有,且特有名词为首个单词,则使用小写,如 apiClient。
  • 其它情况都应当使用该名词原有的写法,如 APIClient、repoID、UserID。
    下面列举了一些常见的特有名词:
// A GonicMapper that contains a list of common initialisms taken from golang/lint
var LintGonicMapper = GonicMapper{"API":   true,"ASCII": true,"CPU":   true,"CSS":   true,"DNS":   true,"EOF":   true,"GUID":  true,"HTML":  true,"HTTP":  true,"HTTPS": true,"ID":    true,"IP":    true,"JSON":  true,"LHS":   true,"QPS":   true,"RAM":   true,"RHS":   true,"RPC":   true,"SLA":   true,"SMTP":  true,"SSH":   true,"TLS":   true,"TTL":   true,"UI":    true,"UID":   true,"UUID":  true,"URI":   true,"URL":   true,"UTF8":  true,"VM":    true,"XML":   true,"XSRF":  true,"XSS":   true,
}

三、声明语句

函数或方法

  • 函数或方法的参数排列顺序遵循以下几点原则(从左到右):

参数的重要程度与逻辑顺序

  • 简单类型优先于复杂类型
  • 尽可能将同种类型的参数放在相邻位置,则只需写一次类型

示例

以下声明语句,User 类型要复杂于 string 类型,但由于 Repository 是 User 的附属品,首先确定 User 才能继而确定 Repository。因此,User 的顺序要优先于 repoName。func IsRepositoryExist(user *User, repoName string) (bool, error) { ...