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 末尾的「常见错误速查表」自查。

如何安装此技能?

访问技能市场,点击「安装」按钮,按提示将技能包放入 AI 编程助手的 skills 目录即可。

浏览技能市场

支持平台:Qoder · QoderWork · Claude · Codex 等 AI 编程助手