google-go-style
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGoogle Go Style — Skill
Google Go 风格指南 — 技能摘要
Derived from the Google Go Style Guide, © Google LLC, licensed CC-BY 3.0. This skill is a derivative digest, not a verbatim reproduction.
Codifies the Google Go Style Guide (canonical + normative + best-practices) into actionable rules.
The full guide is large. This file holds only what should be in your head on every Go change. For deeper rules, load the matching . For unusual situations, WebFetch the source — the guide is normative.
references/*.md改编自Google Go 风格指南,© Google LLC,采用CC-BY 3.0许可。本技能摘要是衍生精简版,并非逐字复制。
将Google Go 风格指南(规范准则+最佳实践)转化为可执行的规则。
完整指南内容庞大,本文仅涵盖每次修改Go代码时需牢记的核心规则。如需更详细的规则,请查看对应的文件。遇到特殊情况时,请直接查阅官方指南——它是权威依据。
references/*.mdQuick Rules
快速规则
Apply on every line of Go you write or review.
编写或评审每一行Go代码时都应遵循以下规则。
Naming
命名规范
- No prefix on getters.
Get, notUser(). Exception: the underlying concept is "get" (HTTP GET).GetUser() - Don't repeat the package name in identifiers. , not
bytes.Buffer.bytes.BytesBuffer, notwidget.New.widget.NewWidget - Receiver names: 1–2 letters, abbreviation of the type, consistent across all methods. Never ,
this,self, orme(unless unused)._, notfunc (c *Config).func (config *Config) - Initialisms keep one case. ,
URL,ID,HTTP. Exported:DB,UserID. Unexported:ServeHTTP,userID. NeverurlPath,Url,Id.Http - Test doubles end in /
Stub/Fake/Spy(or describe behaviour:Mock,AlwaysCharges).AlwaysDeclines - No /
util/common/helperpackage names. They invite import renames at every call site. Name by what the package provides.model - No underscores in identifiers (except test/benchmark/example names, and packages imported only by generated code).
*_test.go - Local variable scope ↔ name length. ,
i,cfor tight loops;db,userCountfor file scope. Don't drop letters to save typing (pollInterval, notSandbox).Sbx
- Getter 不要加前缀。应使用
Get,而非User()。例外情况:底层概念本身就是“获取”(如HTTP GET)。GetUser() - 标识符中不要重复包名。例如,而非
bytes.Buffer;bytes.BytesBuffer,而非widget.New。widget.NewWidget - 接收器名称:1-2个字母,为类型的缩写,且在所有方法中保持一致。绝不要使用、
this、self或me(除非未被使用)。例如_,而非func (c *Config)。func (config *Config) - 首字母缩写词保持统一大小写。例如、
URL、ID、HTTP。导出标识符:DB、UserID;非导出标识符:ServeHTTP、userID。绝不要使用urlPath、Url、Id。Http - 测试替身以/
Stub/Fake/Spy结尾(或描述其行为:Mock、AlwaysCharges)。AlwaysDeclines - 不要使用/
util/common/helper作为包名。这类包名会导致调用时频繁重命名导入。应根据包提供的功能来命名。model - 标识符中不要使用下划线(仅测试/基准测试/示例文件的名称,以及仅被生成代码导入的包除外)。
*_test.go - 局部变量的作用域与名称长度对应。循环中使用、
i、c等短名称;文件级作用域使用db、userCount等长名称。不要为了节省输入而省略字母(如pollInterval,而非Sandbox)。Sbx
Errors
错误处理
- — wrap with
fmt.Errorf("doing X: %w", err)at the end, so the chain prints newest→oldest as%w.outer: middle: inner - only when callers need
%w/errors.Ison the underlying error. Otherwise useerrors.As.%v - at the start is for sentinel wrappers only:
%w. Category first, details after.fmt.Errorf("%w: invalid header", ErrParse) - Sentinel name: at package level:
ErrFoo.var ErrNotFound = errors.New("not found") - Don't duplicate context the underlying error already carries. errors already include the path —
os.Openis wrong; use a higher-level annotation:fmt.Errorf("could not open settings.txt: %v", err).fmt.Errorf("launch codes unavailable: %v", err) - Don't add bare "failed: %v" wrappers. They add no information; just .
return err - Either log it or return it — not both. Pick one. Letting the caller log avoids spam.
- Error strings: lowercase, no trailing punctuation, no .
\n, notfmt.Errorf("something bad happened")."Something bad happened." - Cross-process boundaries (gRPC/RPC): use canonical codes via rather than wrapping internal errors raw with
status.Errorf(codes.X, ...).%w - is the last return value. A function taking
errorshould usually returncontext.Context.error
For more, see .
references/errors.md- — 在末尾使用
fmt.Errorf("执行X: %w", err)进行包装,这样错误链会按从新到旧的顺序打印为%w。outer: middle: inner - 仅当调用者需要使用/
errors.Is检查底层错误时,才使用errors.As。否则使用%w。%v - 放在开头仅用于哨兵错误包装:
%w。先分类,后加细节。fmt.Errorf("%w: 无效头部", ErrParse) - 哨兵错误命名:包级别的:
ErrFoo。var ErrNotFound = errors.New("not found") - 不要重复底层错误已携带的上下文信息。的错误已包含路径——
os.Open是错误的;应使用更高层级的注解:fmt.Errorf("无法打开settings.txt: %v", err)。fmt.Errorf("启动代码不可用: %v", err) - 不要添加无意义的“failed: %v”包装。这类包装没有任何信息,直接即可。
return err - 要么记录日志,要么返回错误——不要同时做。选择其一,让调用者记录日志可避免重复输出。
- 错误字符串:小写,无结尾标点,无。例如
\,而非fmt.Errorf("something bad happened")。"Something bad happened." - 跨进程边界(gRPC/RPC):使用标准错误码,通过实现,而非直接用
status.Errorf(codes.X, ...)包装内部错误。%w - 是最后一个返回值。接收
error的函数通常应返回context.Context。error
如需更多细节,请查看。
references/errors.mdPanics
Panic 使用规范
- Don't panic for normal error handling. Return and multiple return values.
error - over
log.Fatalfor terminal conditions inpanic/main. Fatal does not run deferred functions; that's the point.init - Panics never cross package boundaries in public APIs. Convert to at the API edge with a top-level
errorthat re-panics on unknown payloads.defer recover() - is for package-level vars and tests only.
MustX,MustParse. Not for runtime user input.template.Must - Don't to suppress crashes. Corrupted state propagates further; better is monitoring + crash + fix.
recover() - after
panic("unreachable")is the idiom — the compiler doesn't knowlog.Fatalfdoesn't return.Fatal
For more, see .
references/panics.md- 正常错误处理不要使用panic。应返回和多返回值。
error - 终端场景使用而非
log.Fatal,如panic/main中的致命条件。init不会执行延迟函数,这正是其设计目的。Fatal - 公共API中panic绝不能跨包传播。在API边界处通过顶层将其转换为
defer recover(),并对未知错误重新触发panic。error - 仅用于包级变量和测试。例如
MustX、MustParse。不要用于运行时用户输入。template.Must - 不要通过抑制崩溃。损坏的状态会进一步扩散,更好的方式是监控+崩溃+修复。
recover() - 后使用
log.Fatalf是标准写法——编译器不知道panic("unreachable")不会返回。Fatal
如需更多细节,请查看。
references/panics.mdTests
测试规范
- No assertion libraries / helpers (,
assert.Equal). Userequire.NotNil. For complex types:if got != want { t.Errorf(...) }/cmp.Equalfromcmp.Diff.go-cmp - over
t.Errorby default — keep going, report all failures in one run.t.Fatalonly when continuing is meaningless (setup failed, cascading errors would mislead).t.Fatal - Inside subtests: use
t.Runto skip just that case. Outside subtests in a table loop:t.Fatal+t.Error.continue - NEVER call (
t.Fatal,FailNow,Fatalf) from a goroutine other than the test's own. UseSkipNowfrom worker goroutines;t.Erroronly aftert.Fatalfrom the main goroutine.wg.Wait() - Table-driven tests use named struct fields, not positional: .
{name: "empty", input: "", want: ""} - Test helpers that fail setup call +
t.Helper(). This makes the failure point to the test line, not the helper line.t.Fatalf - Failure message format: — function name, inputs, got, want, in that order.
YourFunc(%v) = %v, want %v
For more, see .
references/tests.md- 不要使用断言库/辅助函数(如、
assert.Equal)。使用require.NotNil。对于复杂类型:使用if got != want { t.Errorf(...) }包中的go-cmp/cmp.Equal。cmp.Diff - 默认使用而非
t.Error——继续执行,一次运行报告所有失败。仅当继续执行无意义时(如初始化失败、级联错误会误导结果)才使用t.Fatal。t.Fatal - 在子测试中:使用
t.Run——仅跳过当前测试用例。在表循环的子测试外部:使用t.Fatal+t.Error。continue - 绝不要从测试主goroutine以外的goroutine调用(
t.Fatal、FailNow、Fatalf)。工作goroutine中使用SkipNow;仅在主goroutine中t.Error之后使用wg.Wait()。t.Fatal - 表驱动测试使用命名结构体字段,而非位置参数:。
{name: "empty", input: "", want: ""} - 初始化失败的测试辅助函数应调用+
t.Helper()。这样失败信息会指向测试代码行,而非辅助函数行。t.Fatalf - 失败消息格式:——按函数名、输入、实际结果、预期结果的顺序排列。
YourFunc(%v) = %v, want %v
如需更多细节,请查看。
references/tests.mdVariables and strings
变量与字符串
- for non-zero init,
:=for zero values.var x T, buti := 42(notvar coords Point).coords := Point{} - , not
var t []stringfor empty slices. Empty slice and nil slice behave the same fort := []string{},len,cap,range.append - vs
new(T): both are fine for zero values;&T{}reads as "zero value placeholder",newis more common when fields are filled.&T{} - Never on
==for slices. Usenil. APIs must not distinguish nil from empty.len(s) == 0 - Strings: for 2–3 short literals;
+for formatting;fmt.Sprintfin loops;strings.Builderfor templates.text/template - Pre-size with /
make([]T, 0, n)only whenmake(map[K]V, n)is known and the code is performance-sensitive. Default to zero-init.n - No shadowing in nested scopes. shadows
if *x { ctx, cancel := ...; }outside thectx. Useifwithctx, cancel = ...and=declared above.var cancel func()
For more, see .
references/strings-and-vars.md- 非零值初始化使用,零值使用
:=。例如var x T,但i := 42(而非var coords Point)。coords := Point{} - 空切片使用,而非
var t []string。空切片和nil切片在t := []string{}、len、cap、range中的行为一致。append - vs
new(T):零值初始化时两者均可;&T{}可理解为“零值占位符”,填充字段时new更常用。&T{} - 切片不要用判断是否为nil。使用
==。API不应区分nil和空切片。len(s) == 0 - 字符串拼接:2-3个短字面量使用;格式化使用
+;循环中使用fmt.Sprintf;模板使用strings.Builder。text/template - 仅当已知大小且代码对性能敏感时,才预分配空间:/
make([]T, 0, n)。默认使用零值初始化。make(map[K]V, n) - 嵌套作用域中不要发生变量遮蔽。例如会遮蔽外部的
if *x { ctx, cancel := ...; }。应在上方声明ctx,然后使用var cancel func()。ctx, cancel = ...
如需更多细节,请查看。
references/strings-and-vars.mdAPI design
API设计
- is the first parameter.
context.Context. Even in test helpers.func F(ctx context.Context, ...) error - Never store in a struct. Pass it through every method that needs it.
context.Context - No global mutable state. Pass dependencies through constructors / function parameters (DI).
- Take interfaces, return concrete types. The consumer of an interface defines it, not the implementer.
- Channel direction in signatures. for receive-only,
<-chan Tfor send-only.chan<- T - Many parameters → option struct (call site reads as labelled fields). 3+ optional / API expected to grow → option struct. Don't put in option structs.
context.Context - Variadic functional options () only when most callers pass nothing, options need parameters, or third parties define options.
...Option - Avoid ,
*stringparameters "to save bytes". Pass values; use pointers for large structs and protobuf messages.*io.Reader
For more, see .
references/api-design.md- 作为第一个参数:
context.Context。即使是测试辅助函数也应遵循。func F(ctx context.Context, ...) error - 绝不要将存储在结构体中。在每个需要它的方法中传递。
context.Context - 不要使用全局可变状态。通过构造函数/函数参数传递依赖(依赖注入)。
- 接收接口类型,返回具体类型。接口由消费者定义,而非实现者。
- 签名中指定通道方向:表示仅接收,
<-chan T表示仅发送。chan<- T - 参数过多时使用选项结构体(调用时可通过标签字段阅读)。3个以上可选参数或API预计会扩展时,使用选项结构体。不要将放入选项结构体。
context.Context - 可变参数选项()仅适用于以下场景:大多数调用者无需传参、选项需要参数、第三方需要定义选项。
...Option - 避免使用、
*string参数来“节省字节”。传递值;大型结构体和protobuf消息使用指针。*io.Reader
如需更多细节,请查看。
references/api-design.mdDocumentation
文档规范
- Doc comments are full sentences starting with the symbol's name.
// Encode writes the JSON encoding of req to w. - Document WHY, not WHAT. The code already says what.
- Document significant sentinel errors and error types the function returns.
- Document cleanup contracts (,
Close, deferred resources).Stop - Document concurrency-safety only when non-obvious (read-only methods are assumed safe; mutating ones are assumed unsafe — say so when that doesn't hold).
- Don't document that cancels the function — that's the contract of
ctx.Done.context.Context
For more, see .
references/documentation.md- 文档注释是完整句子,以符号名称开头:(翻译:// Encode 将req的JSON编码写入w。)
// Encode writes the JSON encoding of req to w. - 记录原因,而非内容。代码本身已经说明了内容。
- 记录函数返回的重要哨兵错误和错误类型。
- 记录清理约定(如、
Close、延迟释放的资源)。Stop - 仅当并发安全性不明显时才记录(只读方法默认安全;修改方法默认不安全——不符合此情况时需说明)。
- 无需记录会取消函数——这是
ctx.Done的约定。context.Context
如需更多细节,请查看。
references/documentation.mdPackage layout
包布局
- for packages not part of the public API.
internal/ - Avoid /
util/common. Name by domain.helper - Imports grouped: stdlib / third-party / proto / blank-side-effect. Blank imports only in or tests.
main - No ever (in Google codebase).
import . - Proto imports get a suffix:
pb.foopb "path/to/foo_go_proto"
For more, see .
references/package-layout.md- 用于不属于公共API的包。
internal/ - 避免使用/
util/common作为包名。按领域命名。helper - 导入分组:标准库/第三方/protobuf/空白导入(仅为副作用)。空白导入仅用于或测试。
main - 绝不要使用(在Google代码库中)。
import . - Protobuf导入添加后缀:
pb。foopb "path/to/foo_go_proto"
如需更多细节,请查看。
references/package-layout.mdDecision Matrices
决策矩阵
%v
vs %w
when wrapping an error
%v%w包装错误时选择%v
还是%w
%v%w| Situation | Choose | Why |
|---|---|---|
Caller will | | Preserves type/sentinel through the chain |
| Crossing an external boundary (RPC, IPC, storage); caller wants canonical codes | | Don't leak internal error types over the wire |
| Logging or human-display only; no programmatic inspection | | |
| Same error is logged here AND returned upward | | Wrapping a logged-then-returned error confuses the chain |
Sentinel categorisation first ( | | Reader sees the category first |
| Adding context around a wrapped error (the common case) | | Chain prints newest→oldest naturally: |
| Underlying error already carries this info | nothing — | Wrapping without adding info is noise |
| Just propagating without analysis | nothing — | Don't wrap for the sake of wrapping |
| 场景 | 选择 | 原因 |
|---|---|---|
调用者需要使用 | | 保留错误链中的类型/哨兵错误 |
| 跨外部边界(RPC、IPC、存储);调用者需要标准错误码 | | 避免内部错误类型泄露到外部 |
| 仅用于日志或人类展示;无需程序检查 | | |
| 同一错误在此处记录日志并向上返回 | | 包装已记录的返回错误会混淆错误链 |
哨兵错误分类优先(如 | | 读者可先看到分类 |
| 为包装错误添加上下文(常见场景) | | 错误链自然按从新到旧打印: |
| 底层错误已包含此信息 | 不处理—— | 无意义的包装只会增加噪音 |
| 仅传播错误不做分析 | 不处理—— | 不要为了包装而包装 |
Function arguments: positional vs option struct vs variadic options
函数参数:位置参数vs选项结构体vs可变参数选项
| Situation | Choose | Why |
|---|---|---|
| ≤ 3 parameters, all required, all distinct types | Positional args | Smallest mechanism |
| Many parameters, most callers set most of them | Option struct (last param) | Self-documenting field names; grows without breaking call sites |
| Many parameters, most callers set none | Variadic options ( | Zero overhead at simple call sites |
| Options need failure validation | Variadic options returning | Can't validate in struct construction |
| Third-party packages must define options | Variadic options with exported | Struct fields can't be extended |
| Same option set used by multiple functions | Option struct | Reuse + share + write helpers on the struct |
| First positional arg, never in option struct | Convention |
| 场景 | 选择 | 原因 |
|---|---|---|
| ≤3个参数,均为必填,类型各不相同 | 位置参数 | 实现最简单 |
| 参数众多,大多数调用者会设置大部分参数 | 选项结构体(最后一个参数) | 字段名自文档化;扩展时不会破坏调用方 |
| 参数众多,大多数调用者无需传参 | 可变参数选项( | 简单调用场景无额外开销 |
| 选项需要失败验证 | 返回 | 结构体构造时无法验证 |
| 第三方包需要定义选项 | 导出 | 结构体字段无法扩展 |
| 多个函数使用同一选项集 | 选项结构体 | 可复用、共享并为结构体编写辅助函数 |
| 第一个位置参数,绝不要放入选项结构体 | 遵循约定 |
panic
vs log.Fatal
vs error
return
paniclog.Fatalerrorpanic
vs log.Fatal
vs 返回error
paniclog.Fatalerror| Situation | Choose | Why |
|---|---|---|
| Library detects normal failure | | Caller decides |
| Library detects an "impossible" invariant violation | | Caller can't recover anyway |
Bad flag/config in | | Stack trace useless; user wants the message |
| Internal package consistency check that has been verified by tests | | More reliable than |
Parser internals that always have a matching | | Plumbing errors through deep recursion is noise |
| Package-level var initializer needs a value derived from a fallible call | | Init-time only; |
| HTTP handler crashes mid-request | never | State is corrupted; let the process crash and restart |
| 场景 | 选择 | 原因 |
|---|---|---|
| 库检测到正常失败 | 返回 | 由调用者决定处理方式 |
| 库检测到“不可能”的不变量违反 | 返回 | 调用者无法恢复 |
| | 堆栈跟踪无用;用户只需错误消息 |
| 内部包一致性检查(已通过测试验证) | | 比 |
解析器内部(API边界有对应的 | 触发私有类型的 | 深层递归中传递错误会产生冗余信息 |
| 包级变量初始化需要从可能失败的调用中获取值 | | 仅在初始化时使用; |
| HTTP请求处理中崩溃 | 绝不要用 | 状态已损坏;让进程崩溃并重启 |
t.Error
vs t.Fatal
vs t.Errorf
+ continue
t.Errort.Fatalt.Errorfcontinuet.Error
vs t.Fatal
vs t.Errorf
+ continue
t.Errort.Fatalt.Errorfcontinue| Situation | Choose |
|---|---|
Multiple independent assertions in one | |
| Setup failure — rest of test cannot proceed | |
| First failure makes subsequent assertions misleading (e.g. encoded ≠ expected, can't decode meaningfully) | |
| Table loop without subtests, this case is broken | |
Inside | |
| Worker goroutine inside a test | |
| Test helper called from main test goroutine | |
| 场景 | 选择 |
|---|---|
单个 | 每个断言使用 |
| 初始化失败——测试剩余部分无法继续 | |
| 首次失败会导致后续断言产生误导(如编码结果≠预期,解码无意义) | |
| 无测试子用例的表循环,当前用例失败 | |
在 | |
| 测试中的工作goroutine | 仅使用 |
| 测试主goroutine调用的辅助函数 | |
Variable declaration form
变量声明形式
| Situation | Choose | Example |
|---|---|---|
| Initializing with a known non-zero value | | |
| Need a zero value, ready for use | | |
Need a | | |
Need a | | |
| Empty slice for return / accumulation | | not |
| Empty map (must be initialized to write) | | nil map can be read but not written |
| Pre-sized slice/map (perf-sensitive, size known) | | Don't over-pre-allocate |
| 场景 | 选择 | 示例 |
|---|---|---|
| 用已知非零值初始化 | | |
| 需要可直接使用的零值 | | |
需要指向零值的 | | |
需要指向带字段值的 | | |
| 用于返回/累加的空切片 | | 而非 |
| 空map(必须初始化才能写入) | | nil map可读但不可写 |
| 预分配空间的切片/map(对性能敏感,已知大小) | | 不要过度预分配 |
Reference Files
参考文件
Load on demand:
- — detailed error structure, wrapping rules, sentinels, RPC boundaries,
references/errors.md.errors.Is/As - — receivers, initialisms, repetition, test doubles, util-package antipattern, shadowing.
references/naming.md - — option struct vs variadic options, channel direction, DI, interfaces, generics.
references/api-design.md - — Test funcs vs helpers, table-driven, subtests, goroutines, acceptance testing,
references/tests.md.cmp - — when allowed (invariants, parsers with recover,
references/panics.md), when forbidden,initvslog.Fatal.log.Exit - — godoc conventions, what to document, what to skip, contexts, errors, cleanup.
references/documentation.md - — package size, file structure, imports,
references/package-layout.md, side-effect imports.internal/ - — concatenation choices, zero values, size hints,
references/strings-and-vars.mdvsnew, shadowing.&T{}
按需查看:
- — 详细的错误结构、包装规则、哨兵错误、RPC边界、
references/errors.md。errors.Is/As - — 接收器、首字母缩写词、重复命名、测试替身、工具包反模式、变量遮蔽。
references/naming.md - — 选项结构体vs可变参数选项、通道方向、依赖注入、接口、泛型。
references/api-design.md - — 测试函数vs辅助函数、表驱动测试、子测试、goroutine、验收测试、
references/tests.md包。cmp - — 允许使用的场景(不变量、带recover的解析器、
references/panics.md)、禁止使用的场景、initvslog.Fatal。log.Exit - — godoc约定、需记录的内容、可忽略的内容、上下文、错误、清理。
references/documentation.md - — 包大小、文件结构、导入、
references/package-layout.md、副作用导入。internal/ - — 拼接选择、零值、大小提示、
references/strings-and-vars.mdvsnew、变量遮蔽。&T{}
Authoritative Sources
权威来源
When in doubt, especially for unusual situations, WebFetch the source section before writing the code. The Google Go Style Guide is the normative authority — this skill is a digest, not a replacement.
- https://google.github.io/styleguide/go/index — overview, normativity definitions
- https://google.github.io/styleguide/go/guide — canonical + normative core principles
- https://google.github.io/styleguide/go/decisions — normative detailed decisions
- https://google.github.io/styleguide/go/best-practices — non-normative patterns and discussions
For Go fundamentals not covered here, read Effective Go.
如有疑问,尤其是遇到特殊情况时,编写代码前请查阅官方指南。Google Go 风格指南是权威依据——本技能摘要是精简版,不能替代官方指南。
- https://google.github.io/styleguide/go/index — 概述、规范定义
- https://google.github.io/styleguide/go/guide — 标准规范核心原则
- https://google.github.io/styleguide/go/decisions — 规范准则详细决策
- https://google.github.io/styleguide/go/best-practices — 非规范性的模式与讨论
如需了解本文未涵盖的Go基础知识,请阅读Effective Go。",