G
Google Go 风格指南
作者:鹿Sir开发工具v1
在编写或修改 Go(.go)文件时强制执行 Google 官方 Go 风格指南。创建、修改或审查任何 Go 代码之前使用本技能。覆盖命名(包、接收者、变量、避免重复)、错误处理(结构、包装、字符串、哨兵错误)、导入(分组、重命名、空白导入)、代码组织(包大小、文件结构)与文档(doc 注释、godoc 规范)。触发词:Go、Golang、Go风格、Go代码审查、Go命名规范、Go错误处理。
下载量
393
点赞
94
价格
免费
技能文档
---
name: meysam81-google-golang-style
description: 在编写或修改 Go(.go)文件时强制执行 Google 官方 Go 风格指南。创建、修改或审查任何 Go 代码之前使用本技能。覆盖命名(包、接收者、变量、避免重复)、错误处理(结构、包装、字符串、哨兵错误)、导入(分组、重命名、空白导入)、代码组织(包大小、文件结构)与文档(doc 注释、godoc 规范)。触发词:Go、Golang、Go风格、Go代码审查、Go命名规范、Go错误处理。
title: Google Go 风格指南
category: 开发工具
---
# Google Go 风格指南
本技能将 Google 官方 Go 风格指南提炼为可执行的规则。指南按以下优先级权衡:**清晰 > 简单 > 简洁 > 可维护 > 一致**。
清晰意味着读者能理解代码做什么、为什么这样做;简单意味着不引入不必要的抽象就能达成目标。这两点高于其他一切。
## 技能工作流
### 步骤1:编码前确认适用规则
开始写 Go 代码前,先浏览本指南的命名、错误处理、导入、文档、代码组织各章节,确认将使用的约定。
### 步骤2:编码中应用规则
按各章节规则编写代码:命名遵循 MixedCaps 与去重复原则;错误始终作为最后一个返回值并用 `%w` 包装;导入按四组排列;导出符号都写 doc 注释。
### 步骤3:提交前自查
对照「常见错误速查表」逐项检查,并确认 `gofmt` 输出无差异。
## 格式
所有 Go 源文件必须符合 `gofmt` 输出。绝不手动调整 `gofmt` 能处理的格式。
多词命名一律使用 `MixedCaps` 或 `mixedCaps`。禁止 `snake_case` 或 `SCREAMING_SNAKE_CASE`,常量也不例外。
```go
// 正确。
const MaxPacketSize = 512
var userCount int
// 错误。
const MAX_PACKET_SIZE = 512
var user_count int
```
## 行长
没有固定行长限制。觉得某行过长时,优先重构(提取变量、辅助函数)而非换行拆分。如果已经短到实际极限,就让它保持长。
不要在缩进变化处(函数声明、条件语句)之前换行。不要把长字符串(如 URL)拆成多行。
函数参数换行时按语义分组,而不是按列宽对齐:
```go
// 按语义分组。
canvas.RenderHeptagon(
fillColor,
x0, y0, vertexColor0,
x1, y1, vertexColor1,
// ...
)
```
## 命名
命名在使用时不应感到重复,应结合上下文,不重复已经清晰的概念。
### 包名
- 仅小写,无下划线,无 mixedCaps:`tabwriter` 而非 `tabWriter`
- 名字描述它提供什么,而不是包含什么
- 避免 `util`、`helper`、`common`、`model`——这些在调用处毫无信息量
- 避免容易被局部变量遮蔽的名字:用 `usercount` 而非 `count`
```go
// 好:调用处一目了然。
db := spannertest.NewDatabaseFromFile(...)
b := elliptic.Marshal(curve, x, y)
// 差:没有信息量。
db := test.NewDatabaseFromFile(...)
b := helper.Marshal(curve, x, y)
```
### 接收者名
短(1-2 个字母)、类型缩写、所有方法保持一致:
```go
func (t Tray) Size() int // 而非 (tray Tray) 或 (this Tray)
func (ri *ResearchInfo) Update() // 而非 (info *ResearchInfo)
func (s *Scanner) Next() Token // 而非 (self *Scanner)
```
### 变量名
长度与作用域成正比,与使用频率成反比:
- 小作用域(1-7 行):单字母或短词(`i`、`c`、`r`)
- 中作用域(8-15 行):单个描述性词(`count`、`users`)
- 大作用域(15-25 行):多词(`userCount`、`projectName`)
- 特大作用域(25+ 行):完整描述性命名
常见类型的惯用缩写:`r` 表示 `io.Reader`/`*http.Request`,`w` 表示 `io.Writer`/`http.ResponseWriter`,`ctx` 表示 `context.Context`。
名字中省略类型:
```go
var users int // 而非 numUsers、usersInt
var name string // 而非 nameString
var primary *Project // 而非 primaryProject
```
### 避免重复
这是影响力最大的命名规则。重复会让代码充满噪音、更难读。
**包名 vs 导出名** —— 符号名中不要重复包名:
```go
// 好。
widget.New() // 而非 widget.NewWidget()
db.Load() // 而非 db.LoadFromDatabase()
// 如果包只导出一个与包同名的类型,构造函数就叫 New。
```
**方法名 vs 接收者** —— 不重复接收者类型:
```go
func (c *Config) WriteTo(w io.Writer) // 而非 WriteConfigTo
func (p *Project) Name() string // 而非 ProjectName()
```
**上下文 vs 局部名** —— 去掉上下文已经提供的信息:
```go
// 在 "sqldb" 包中:
type Connection struct{} // 而非 DBConnection
// 在 *DB 的方法中:
func (db *DB) UserCount() (int, error) {
var count int64 // 而非 userCountInt64
if err := db.Load("count(distinct users)", &count); err != nil {
return 0, fmt.Errorf("load user count: %s", err)
}
return int(count), nil
}
```
### 首字母缩略词
缩略词大小写保持一致:`URL` 或 `url`,绝不 `Url`;`ID` 而非 `Id`;`HTTP` 而非 `Http`。
| 作用域 | 正确 | 错误 |
| ---------- | ---------------------------- | ---------------------------- |
| 导出 | `XMLAPI`, `ID`, `DB`, `GRPC` | `XmlApi`, `Id`, `Db`, `Grpc` |
| 未导出 | `xmlAPI`, `id`, `db`, `gRPC` | `xmlapi`, `iD`, `dB`, `grpc` |
### Getter
不加 `Get` 前缀,直接用名词:
```go
func (c *Config) Name() string // 而非 GetName()
func (u *User) Counts() int // 而非 GetCounts()
```
操作开销大或涉及 I/O 时用 `Compute` 或 `Fetch`,提示调用方可能阻塞。
### 常量
只用 MixedCaps。按用途命名,不按值命名:
```go
const MaxPacketSize = 512 // 而非 MAX_PACKET_SIZE,也非 kMaxPacketSize
const ExecuteBit = 1 << iota // 而非 Twelve = 12
```
## 错误处理
### 返回错误
`error` 永远是最后一个返回值。成功时返回 `nil`。导出函数始终返回 `error` 接口,绝不返回具体错误类型。
```go
// 好。
func Lookup() (*Result, error)
// 差:具体错误类型可能引发接口内 nil 指针 bug。
func Bad() *os.PathError
```
### 错误字符串
不大写(除非以导出名或专有名词开头),结尾不加标点。
```go
err := fmt.Errorf("something bad happened") // 好。
err := fmt.Errorf("Something bad happened.") // 差。
```
完整展示消息(日志、测试失败输出、UI)应大写开头:
```go
log.Errorf("Operation aborted: %v", err)
t.Errorf("Op(%q) failed unexpectedly; err=%v", args, err)
```
### 包装错误
调用方需要用 `errors.Is`/`errors.As` 检查底层错误时用 `%w`;只想附加上下文但隐藏错误链时用 `%v`(尤其在 RPC 等系统边界处)。
附加底层错误没有提供的上下文,不要重复信息:
```go
// 好:附加了有意义的上下文。
if err := os.Open("settings.txt"); err != nil {
return fmt.Errorf("launch codes unavailable: %w", err)
}
// 差:重复了 os.Open 错误中已有的文件名。
if err := os.Open("settings.txt"); err != nil {
return fmt.Errorf("could not open settings.txt: %w", err)
}
// 差:注释没有增加任何信息。
return fmt.Errorf("failed: %w", err) // 直接 return err 即可
```
### 哨兵错误
为调用方需要区分的预期情况定义包级哨兵错误,用 `errors.Is()` 判断(可穿透包装)。
```go
var (
ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New("unauthorized")
)
// 调用方用 errors.Is 判断,而非 ==。
if errors.Is(err, ErrNotFound) {
// 处理资源缺失
}
```
### 处理每个错误
除非函数文档声明永不失败,否则不要用 `_` 丢弃错误。确要丢弃时注释原因:
```go
n, _ := b.Write(p) // 永远不会返回非 nil 错误
```
否则要么处理、要么返回,特殊情况下 `log.Fatal`。不要 `panic`。
### 避免带内错误
不要用 -1、空字符串或 nil 表示错误,使用多返回值:
```go
// 好。
func Lookup(key string) (value string, ok bool)
// 差:调用方无法区分「未找到」和空字符串。
func Lookup(key string) string
```
## 导入
### 分组
四组,用空行分隔,按此顺序:
1. 标准库
2. 第三方 / 项目内包
3. Protocol buffer 导入(带 `pb` 后缀)
4. 副作用导入(`_ "package"`)
```go
import (
"fmt"
"os"
"github.com/user/project/internal/config"
"golang.org/x/text/encoding"
foopb "myproj/foo/proto/proto"
_ "myproj/rpc/protocols/dial"
)
```
### 重命名
尽量避免重命名导入。合理理由:
- 与另一个导入名冲突
- 生成的 proto 包(必须重命名以去除下划线并加 `pb` 后缀)
- 名字无信息量(如 `v1`)——重命名为描述性名称:`core "k8s.io/api/core/v1"`
- 与常见局部变量冲突——加 `pkg` 后缀:`urlpkg`
### 空白导入与点导入
空白导入(`_ "package"`)只允许出现在 `main` 包或测试中,库代码中禁止。
绝不使用点导入(`import . "package"`),它会掩盖符号来源。
## 文档
### Doc 注释
所有导出名必须有 doc 注释,以被描述对象的名称开头,使用完整句子。
```go
// A Request represents a request to run a command.
type Request struct { ... }
// Encode writes the JSON encoding of req to w.
func Encode(w io.Writer, req *Request) { ... }
```
行为不直观的未导出类型也应写 doc 注释。
### 注释风格
Doc 注释:完整句子,大写开头,带标点。
结构体字段的行尾注释:可以是片段。
```go
type Server struct {
// BaseDir points to the base directory for data storage.
BaseDir string
WelcomeMessage string // displayed when user logs in
ProtocolVersion string // checked against incoming requests
PageLength int // optional; default: 20
}
```
注释行长以 80 列终端可读为目标,不做硬性限制。
### 包注释
紧挨 `package` 子句上方,中间不空行:
```go
// Package math provides basic constants and mathematical functions.
package math
```
每个包一条包注释。`main` 包描述命令本身。
## 代码组织
### 简单优先
用最少的机制解决问题。优先使用语言核心构造(channel、slice、map、循环、struct)而非第三方库。加依赖前先查标准库——`map[string]bool` 做集合就够了,不必引入集合库。
### 包大小
- 把实现紧密耦合的类型放在同一个包
- 如果用户必须同时导入两个包才能有意义地使用其一,就合并它们
- 不要把所有东西塞进一个包——概念上独立的事物各得其所
- 没有「一个类型一个文件」的规定。文件应足够聚焦,维护者能判断哪个文件有什么
### 测试替身
测试替身放在独立的 `<package>test` 包中(如 `creditcardtest`)。有多个替身时按行为命名:
```go
package creditcardtest
// AlwaysCharges stubs creditcard.Service and simulates success.
type AlwaysCharges struct{}
func (AlwaysCharges) Charge(*creditcard.Card, money.Money) error { return nil }
// AlwaysDeclines stubs creditcard.Service and simulates declined charges.
type AlwaysDeclines struct{}
func (AlwaysDeclines) Charge(*creditcard.Card, money.Money) error {
return creditcard.ErrDeclined
}
```
### 快乐路径
让成功路径直线下行。先处理错误再继续主逻辑,避免把成功场景嵌套在条件里。
```go
// 好:快乐路径直线下行。
func Process(id string) (*User, error) {
user, err := db.GetUser(id)
if err != nil {
return nil, fmt.Errorf("get user %s: %w", id, err)
}
if err := user.Validate(); err != nil {
return nil, fmt.Errorf("validate user %s: %w", id, err)
}
return user, nil
}
```
### 遮蔽
在新作用域小心使用 `:=`——它会创建遮蔽外层同名变量的新变量。这是 `ctx` 和 `err` 相关 bug 的常见来源:
```go
// Bug:if 内的 ctx 是新变量;外层 ctx 未变。
if *shortenDeadlines {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second) // 遮蔽!
defer cancel()
}
// 这里的 ctx 仍是原来的、没有超时限制的 context。
// 修复:单独声明 cancel,用 = 而非 :=。
if *shortenDeadlines {
var cancel func()
ctx, cancel = context.WithTimeout(ctx, 3*time.Second)
defer cancel()
}
```
## 常见错误速查表
| 错误 | 正确 |
| ---------------------------------- | ----------------------------------------- |
| `MAX_PACKET_SIZE` | `MaxPacketSize` |
| `widget.NewWidget()` | `widget.New()` |
| `func (c *Config) GetName()` | `func (c *Config) Name()` |
| `var numUsers int` | `var users int` |
| `return err`(裸返回无上下文) | `return fmt.Errorf("operation: %w", err)` |
| `err := fmt.Errorf("Failed.")` | `err := fmt.Errorf("failed")` |
| `func Bad() *os.PathError` | `func Bad() error` |
| 按 80/100/120 列强行换行 | 过长就重构,否则保持原样 |
| `import . "foo"` | `import "foo"` 并限定调用:`foo.Bar()` |
| `package util` | 按其提供的能力命名 |使用说明
# Google Go 风格指南 在编写、修改或审查 Go 代码时强制执行 Google 官方 Go 风格指南。 ## 能做什么 - 统一命名规范:包名、接收者、变量、缩略词,消除重复命名 - 规范错误处理:错误包装、哨兵错误、带内错误规避 - 规范导入分组、重命名与空白导入 - 指导包组织、测试替身放置与 doc 注释书写 ## 何时使用 - 新建或修改任何 `.go` 文件之前 - Go 代码审查、重构、架构评审时 - 团队需要统一 Go 编码约定时 ## 快速上手 让 Agent 在写 Go 代码前加载本技能,例如: ```text 按照 Google Go 风格指南帮我审查这段代码,重点看命名重复和错误包装。 ``` 核心原则:**清晰 > 简单 > 简洁 > 可维护 > 一致**。提交前对照 SKILL.md 末尾的「常见错误速查表」自查。
支持平台:Qoder · QoderWork · Claude · Codex 等 AI 编程助手