Go 编码规范指南
1. 前言
本规范基于 Google Go Style Guide 和业界最佳实践,旨在建立统一的编码风格,提高代码可读性、可维护性和团队协作效率。
规范等级说明
必须(MUST): 强制要求,必须遵守
推荐(SHOULD): 建议遵守,特殊情况可例外
可选(MAY): 可参考使用
2. 代码格式化
2.1 【必须】使用 gofmt
所有代码必须使用 gofmt 或 goimports 格式化。
2.2 【推荐】行长度
建议单行不超过 120 个字符
超长行合理换行,保持可读性
例外情况:
import 语句
工具生成的代码
struct tag
长字符串字面量
2.3 【必须】空格和运算符
// ✅ 正确:运算符前后留空格
result := a + b * c
array[index] = value
// ✅ 正确:函数调用和数组索引紧凑
fmt.Printf("value: %d", arr[i])
// ❌ 错误:缺少空格
result:=a+b*c3. 导入规范
3.1 【必须】使用 goimports
使用
goimports自动管理导入按字母顺序排序
分组管理,空行分隔
3.2 【必须】导入分组
import (
// 第一组:标准库
"context"
"fmt"
"os"
// 第二组:第三方库
"github.com/gin-gonic/gin"
"github.com/spf13/cobra"
// 第三组:内部包
"myproject/internal/config"
"myproject/pkg/utils"
// 第四组:匿名导入(可选)
// import mysql driver
_ "github.com/go-sql-driver/mysql"
)3.3 【必须】路径规范
// ✅ 正确:使用完整路径
import "github.com/myproject/pkg/utils"
// ❌ 错误:使用相对路径
import "../utils"3.4 【推荐】别名使用
// ✅ 仅在必要时使用别名
import (
"fmt"
"os"
// 包名冲突时使用别名
nettrace "golang.org/x/net/trace"
"runtime/trace"
// 包名不规范时使用别名
xapi "github.com/some/X_Y_Z_API"
)4. 错误处理
4.1 【必须】错误处理原则
// ✅ 正确:error 作为最后一个返回值
func processData(input string) (result Data, err error) {
// 实现
}
// ✅ 正确:立即处理错误
data, err := processData(input)
if err != nil {
return fmt.Errorf("处理数据失败: %w", err)
}
// 使用 data
// ❌ 错误:error 不是最后一个参数
func badFunction() (error, string) { }
// ❌ 错误:else 分支处理正常逻辑
if err != nil {
// 错误处理
} else {
// 正常逻辑
}4.2 【推荐】错误创建
// ✅ 简单错误
return errors.New("配置文件不存在")
// ✅ 格式化错误(Go 1.13+)
return fmt.Errorf("无法连接到服务器 %s: %w", addr, err)
// ✅ 自定义错误类型
type ValidationError struct {
Field string
Value interface{}
}
func (e ValidationError) Error() string {
return fmt.Sprintf("字段 %s 的值 %v 无效", e.Field, e.Value)
}4.3 【必须】panic 和 recover
// ✅ 正确:仅在不可恢复的情况下使用 panic
func main() {
config, err := loadConfig()
if err != nil {
log.Fatalf("无法加载配置: %v", err)
}
// 继续执行
}
// ✅ 正确:goroutine 中捕获 panic
func worker() {
defer func() {
if r := recover(); r != nil {
log.Printf("worker panic: %v\n%s", r, debug.Stack())
}
}()
// 工作逻辑
}
// ❌ 错误:在业务逻辑中使用 panic
func calculateDiscount(price float64) float64 {
if price < 0 {
panic("价格不能为负数") // 应该返回错误
}
return price * 0.1
}4.4 【必须】类型断言
// ✅ 正确:使用 comma ok 模式
if str, ok := value.(string); ok {
// 使用 str
} else {
// 处理类型不匹配
}
// ❌ 错误:可能产生 panic
str := value.(string)5. 命名规范
5.1 【必须】通用命名规则
使用驼峰命名法(camelCase/PascalCase)
导出成员首字母大写,私有成员首字母小写
避免使用缩写,除非是广泛认知的
5.2 【必须】包命名
// ✅ 正确
package user
package httputil
package stringutil
// ❌ 错误
package userManager
package utils // 太泛化
package http_util // 使用下划线5.3 【必须】文件命名
// ✅ 正确
user_service.go
http_client.go
config_parser.go
// ❌ 错误
userService.go
HTTPClient.go5.4 【必须】变量和函数命名
// ✅ 正确:专有名词规则
var apiClient *APIClient
var userID int64
var httpServer *HTTPServer
// 私有变量
var dbConn *sql.DB
var configFile string
// ❌ 错误
var apiClient *ApiClient // API 应该全大写
var userId int64 // ID 应该全大写
var UrlPath string // URL 在开头应该小写5.5 【必须】常量命名
// ✅ 单个常量
const DefaultTimeout = 30 * time.Second
// ✅ 枚举常量
type Status int
const (
StatusPending Status = iota
StatusRunning
StatusCompleted
StatusFailed
)
// ✅ 私有常量
const maxRetryCount = 35.6 【必须】结构体和接口命名
// ✅ 结构体:名词或名词短语
type UserService struct {
db *sql.DB
}
type HTTPClient struct {
timeout time.Duration
}
// ✅ 接口命名
type Reader interface {
Read([]byte) (int, error)
}
type UserRepository interface {
FindByID(id int64) (*User, error)
Create(user *User) error
}
// ❌ 错误
type UserProcessor struct {} // 应该避免动词
type DataInfo struct {} // Info 太泛化6. 代码结构
6.1 【推荐】控制结构最佳实践
if 语句
// ✅ 推荐:初始化语句
if err := validate(input); err != nil {
return err
}
// ✅ 推荐:变量在左,常量在右
if count > 0 {
// 处理逻辑
}
// ✅ 推荐:布尔值直接判断
if isValid {
// 处理逻辑
}
if !isEnabled {
return
}for 和 range
// ✅ 推荐:使用短变量声明
for i := 0; i < len(items); i++ {
process(items[i])
}
// ✅ 仅需要 key
for key := range m {
delete(m, key)
}
// ✅ 仅需要 value
for _, value := range slice {
process(value)
}switch 语句
// ✅ 必须包含 default
switch status {
case StatusPending:
// 处理等待状态
case StatusRunning:
// 处理运行状态
default:
// 处理未知状态
log.Printf("未知状态: %v", status)
}6.2 【推荐】提前返回
// ✅ 推荐:提前返回错误
func processUser(userID int64) error {
user, err := findUser(userID)
if err != nil {
return fmt.Errorf("查找用户失败: %w", err)
}
if user.IsBlocked {
return errors.New("用户已被锁定")
}
// 主要逻辑
return updateUser(user)
}7. 函数设计
7.1 【推荐】函数参数
// ✅ 推荐:命名返回值(多个相同类型)
func parseCoordinate(input string) (lat, lng float64, err error) {
// 实现
}
// ✅ 推荐:普通返回值
func getUser(id int64) (*User, error) {
// 实现
}
// ✅ 推荐:参数数量不超过 5 个
func createUser(name, email string, age int, isActive bool) (*User, error) {
// 实现
}
// ✅ 考虑使用配置结构体
type CreateUserRequest struct {
Name string
Email string
Age int
IsActive bool
Tags []string
}
func createUserAdvanced(req CreateUserRequest) (*User, error) {
// 实现
}7.2 【必须】资源管理
// ✅ 正确:检查错误后再 defer
func readFile(filename string) ([]byte, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close() // 确认打开成功后再 defer
return io.ReadAll(file)
}
// ✅ 循环中的资源管理
func processFiles(filenames []string) error {
for _, filename := range filenames {
if err := func() error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
return processFile(file)
}(); err != nil {
return err
}
}
return nil
}7.3 【推荐】方法接收器
// ✅ 推荐:接收器命名
type UserService struct {
db *sql.DB
}
// 短方法可以使用单字符
func (u *UserService) Create(user *User) error {
// 实现
}
// 长方法使用有意义的名称
func (service *UserService) generateComplexReport() (*Report, error) {
// 超过 20 行的方法
}
// ❌ 错误:避免使用这些名称
func (self *UserService) BadMethod() {}
func (this *UserService) BadMethod() {}
func (me *UserService) BadMethod() {}7.4 【必须】代码度量限制
文件长度: 不超过 800 行
函数长度: 不超过 80 行
嵌套深度: 不超过 4 层
函数参数: 不超过 5 个
7.5 【必须】魔法数字
// ❌ 错误:重复的魔法数字
func calculateArea(radius float64) float64 {
return 3.14159 * radius * radius
}
func calculateCircumference(radius float64) float64 {
return 2 * 3.14159 * radius
}
// ✅ 正确:使用常量
const Pi = 3.14159
func calculateArea(radius float64) float64 {
return Pi * radius * radius
}
func calculateCircumference(radius float64) float64 {
return 2 * Pi * radius
}8. 注释规范
8.1 【必须】包注释
// Package user 提供用户管理相关的核心功能,包括用户创建、
// 认证、权限管理等操作。
package user
/*
Package config 提供应用程序配置管理功能。
支持多种配置源:
- 配置文件 (JSON, YAML, TOML)
- 环境变量
- 命令行参数
基本用法:
cfg, err := config.Load("app.yaml")
if err != nil {
log.Fatal(err)
}
*/
package config8.2 【必须】函数注释
// CreateUser 创建新用户并返回用户信息。
// 如果邮箱已存在,返回 ErrEmailExists 错误。
// 如果验证失败,返回 ValidationError。
func CreateUser(req CreateUserRequest) (*User, error) {
// 实现
}
// 例外:某些标准方法可以不写注释
func (u *User) String() string {
return fmt.Sprintf("User{ID: %d, Name: %s}", u.ID, u.Name)
}8.3 【必须】类型注释
// User 表示系统中的用户实体
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
// CreatedAt 用户创建时间
CreatedAt time.Time `json:"created_at"`
}
// UserStatus 定义用户状态类型
type UserStatus int
// UserRepository 定义用户数据访问接口
type UserRepository interface {
// FindByID 根据用户ID查找用户
FindByID(id int64) (*User, error)
// Create 创建新用户
Create(user *User) error
}8.4 【必须】常量和变量注释
// DefaultTimeout 默认请求超时时间
const DefaultTimeout = 30 * time.Second
// 用户状态定义
const (
UserStatusActive UserStatus = 1 // 活跃用户
UserStatusInactive UserStatus = 2 // 非活跃用户
UserStatusBlocked UserStatus = 3 // 被锁定用户
)
// ErrUserNotFound 用户不存在错误
var ErrUserNotFound = errors.New("用户不存在")9. 测试规范
9.1 【必须】测试文件规范
// 文件命名:xxx_test.go
// user_service_test.go
func TestUserService_Create(t *testing.T) {
// 测试实现
}
func Test_validateEmail(t *testing.T) {
// 私有函数测试
}
func TestUser_String(t *testing.T) {
// 方法测试
}9.2 【推荐】测试结构
func TestCreateUser(t *testing.T) {
tests := []struct {
name string
input CreateUserRequest
want *User
wantErr bool
}{
{
name: "valid user",
input: CreateUserRequest{
Name: "张三",
Email: "[email protected]",
},
want: &User{
Name: "张三",
Email: "[email protected]",
},
wantErr: false,
},
{
name: "invalid email",
input: CreateUserRequest{
Name: "李四",
Email: "invalid-email",
},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := CreateUser(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("CreateUser() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("CreateUser() = %v, want %v", got, tt.want)
}
})
}
}10. 依赖管理
10.1 【必须】使用 Go Modules
# 初始化模块
go mod init github.com/company/project
# 整理依赖
go mod tidy
# 验证依赖
go mod verify10.2 【推荐】版本管理
// go.mod 示例
module github.com/company/project
go 1.21
require (
github.com/gin-gonic/gin v1.9.1
github.com/spf13/cobra v1.7.0
)
require (
// 间接依赖...
)10.3 【必须】提交规范
必须提交:
go.mod和go.sum不要提交:
vendor/目录(除非特定需求)建议: 定期执行
go mod tidy清理依赖
11. 项目结构建议
11.1 【推荐】标准项目布局
project/
├── cmd/ # 主应用程序入口
│ └── server/
│ └── main.go
├── internal/ # 私有应用程序代码
│ ├── config/
│ ├── handler/
│ ├── service/
│ └── repository/
├── pkg/ # 可被外部使用的库代码
│ └── utils/
├── api/ # API 定义文件
├── docs/ # 设计和用户文档
├── scripts/ # 构建、安装、分析等脚本
├── test/ # 额外的外部测试应用程序和测试数据
├── go.mod
├── go.sum
└── README.md12. 常用工具
12.1 【推荐】开发工具
# 格式化代码
gofmt -w .
goimports -w .
# 静态分析
go vet ./...
golint ./...
# 安全检查
gosec ./...
# 依赖检查
go mod tidy
go mod verify
# 测试
go test ./...
go test -race ./...
go test -cover ./...12.2 【推荐】编辑器配置
建议配置编辑器/IDE 在保存时自动执行:
goimports
go vet
golint
13. 性能建议
13.1 【推荐】避免常见性能陷阱
// ✅ 推荐:预分配切片容量
items := make([]Item, 0, expectedSize)
// ✅ 推荐:使用字符串构建器
var builder strings.Builder
builder.Grow(expectedSize) // 预分配
for _, item := range items {
builder.WriteString(item.String())
}
result := builder.String()
// ✅ 推荐:避免在循环中进行字符串拼接
// ❌ 错误
var result string
for _, item := range items {
result += item.String() // 每次都会重新分配内存
}13.2 【推荐】并发最佳实践
// ✅ 推荐:使用 context 控制超时和取消
func fetchData(ctx context.Context, url string) (*Data, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 处理响应
}
// ✅ 推荐:使用 sync.Pool 复用对象
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
func processData(data []byte) error {
buf := bufferPool.Get().([]byte)
defer bufferPool.Put(buf)
// 使用 buf 处理数据
}14. 安全建议
14.1 【必须】输入验证
// ✅ 推荐:验证输入参数
func updateUser(userID int64, updates map[string]interface{}) error {
if userID <= 0 {
return errors.New("无效的用户ID")
}
// 验证允许的字段
allowedFields := map[string]bool{
"name": true,
"email": true,
"age": true,
}
for field := range updates {
if !allowedFields[field] {
return fmt.Errorf("不允许更新字段: %s", field)
}
}
// 执行更新
}14.2 【必须】敏感信息处理
// ✅ 推荐:不在日志中记录敏感信息
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"-"` // 不序列化密码
}
func (u User) String() string {
return fmt.Sprintf("User{ID: %d, Name: %s, Email: %s}",
u.ID, u.Name, u.Email) // 不包含密码
}最后更新于