go-style-core
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Style Core Principles
Go语言核心编码风格原则
Normative: This guidance is required per Google's canonical Go style guide.
规范性要求:本指南为Google官方Go语言风格强制要求内容
Style Principles (Priority Order)
编码风格原则(优先级顺序)
When writing readable Go code, apply these principles in order of importance:
编写易读的Go代码时,请按以下优先级应用这些原则:
1. Clarity
1. 清晰性
The code's purpose and rationale must be clear to the reader.
- What: Use descriptive names, helpful comments, and efficient organization
- Why: Add commentary explaining rationale, especially for nuances
- View clarity through the reader's lens, not the author's
- Code should be easy to read, not easy to write
go
// Good: Clear purpose
func (c *Config) WriteTo(w io.Writer) (int64, error)
// Bad: Unclear, repeats receiver
func (c *Config) WriteConfigTo(w io.Writer) (int64, error)代码的用途和设计思路必须让阅读者一目了然。
- 要点:使用描述性命名、有帮助的注释,以及合理的代码组织方式
- 缘由:添加注释解释设计思路,尤其是针对细节差异部分
- 从阅读者的视角而非作者视角判断代码是否清晰
- 代码应易于阅读,而非易于编写
go
// 推荐:用途清晰
func (c *Config) WriteTo(w io.Writer) (int64, error)
// 不推荐:含义模糊,重复了接收者名称
func (c *Config) WriteConfigTo(w io.Writer) (int64, error)2. Simplicity
2. 简洁性
Code should accomplish goals in the simplest way possible.
Simple code:
- Is easy to read top to bottom
- Does not assume prior knowledge
- Has no unnecessary abstraction levels
- Has comments explaining "why", not "what"
- May be mutually exclusive with "clever" code
Least Mechanism Principle: Prefer standard tools:
- Core language constructs (channel, slice, map, loop, struct)
- Standard library tools (http client, template engine)
- Core libraries before new dependencies
代码应以最简单的方式实现目标。
简洁的代码:
- 从上到下阅读流畅
- 不假设阅读者具备前置知识
- 无不必要的抽象层级
- 注释解释“为什么”而非“是什么”
- 可能与“巧妙”的代码互斥
最小机制原则:优先使用标准工具:
- 核心语言构造(channel、slice、map、循环、struct)
- 标准库工具(http客户端、模板引擎)
- 优先使用核心库,再考虑引入新依赖
3. Concision
3. 简洁性(高信噪比)
Code should have high signal-to-noise ratio.
- Avoid repetitive code
- Avoid extraneous syntax
- Avoid unnecessary abstraction
- Use table-driven tests to factor out common code
go
// Good: Common idiom, high signal
if err := doSomething(); err != nil {
return err
}
// Good: Signal boost for unusual case
if err := doSomething(); err == nil { // if NO error
// ...
}代码应具备高信噪比。
- 避免重复代码
- 避免冗余语法
- 避免不必要的抽象
- 使用表驱动测试提取公共代码
go
// 推荐:通用惯用写法,信噪比高
if err := doSomething(); err != nil {
return err
}
// 推荐:针对特殊场景强化信号
if err := doSomething(); err == nil { // 无错误时
// ...
}4. Maintainability
4. 可维护性
Code is edited many more times than written.
Maintainable code:
- Is easy for future programmers to modify correctly
- Has APIs that grow gracefully
- Uses predictable names (same concept = same name)
- Minimizes dependencies
- Has comprehensive tests with clear diagnostics
go
// Bad: Critical detail hidden
if user, err = db.UserByID(userID); err != nil { // = vs :=
// Good: Explicit and clear
u, err := db.UserByID(userID)
if err != nil {
return fmt.Errorf("invalid origin user: %s", err)
}
user = u代码被修改的次数远多于被编写的次数。
可维护的代码:
- 便于后续开发者正确修改
- API可优雅扩展
- 使用可预测的命名(同一概念使用相同名称)
- 最小化依赖
- 具备诊断清晰的全面测试
go
// 不推荐:关键细节被隐藏
if user, err = db.UserByID(userID); err != nil { // = 而非 :=
// 推荐:明确清晰
u, err := db.UserByID(userID)
if err != nil {
return fmt.Errorf("invalid origin user: %s", err)
}
user = u5. Consistency
5. 一致性
Code should look and behave like similar code in the codebase.
- Package-level consistency is most important
- When ties occur, break in favor of consistency
- Never override documented style principles for consistency
代码的外观和行为应与代码库中类似代码保持一致。
- 包级别的一致性最为重要
- 出现分歧时,优先保证一致性
- 切勿为了一致性而违反已明确的风格原则
Formatting
格式规范
gofmt is Required
必须使用gofmt
All Go source files must conform to output. No exceptions.
gofmtbash
undefined所有Go源文件必须符合的输出格式,无例外。
gofmtbash
undefinedFormat a file
格式化单个文件
gofmt -w myfile.go
gofmt -w myfile.go
Format all files in directory
格式化目录下所有文件
gofmt -w .
undefinedgofmt -w .
undefinedParentheses
括号使用
Source: Effective Go
Go needs fewer parentheses than C and Java. Control structures (, , ) don't have parentheses in their syntax. The operator precedence hierarchy is shorter and clearer, so means what the spacing suggests—unlike in other languages.
ifforswitchx<<8 + y<<16来源:Effective Go
Go所需的括号比C和Java更少。控制结构(、、)的语法中不需要括号。运算符优先级体系更短更清晰,因此的含义与空格暗示的一致——与其他语言不同。
ifforswitchx<<8 + y<<16MixedCaps (Camel Case)
MixedCaps(驼峰命名)
Go uses or , never underscores:
MixedCapsmixedCapsgo
// Good
MaxLength // exported constant
maxLength // unexported constant
userID // variable
// Bad
MAX_LENGTH // no snake_case
max_length // no underscoresExceptions:
- Test function names may use underscores:
TestFoo_Bar - Generated code interoperating with OS/cgo
Go使用或命名,绝不使用下划线:
MixedCapsmixedCapsgo
// 推荐
MaxLength // 导出常量
maxLength // 未导出常量
userID // 变量
// 不推荐
MAX_LENGTH // 禁止蛇形命名
max_length // 禁止使用下划线例外情况:
- 测试函数名称可使用下划线:
TestFoo_Bar - 与操作系统/cgo交互的自动生成代码
Line Length
行长度
There is no rigid line length limit in Go, but avoid uncomfortably long
lines. Uber suggests a soft limit of 99 characters.
Combined: Google + Uber + Go Wiki CodeReviewComments guidance
Guidelines:
- If a line feels too long, refactor rather than just wrap
- Don't split before indentation changes (function declarations, conditionals)
- Don't split long strings (URLs) into multiple lines
- When splitting, put all arguments on their own lines
- If it's already as short as practical, let it remain long
Break by semantics, not length:
Advisory: Go Wiki CodeReviewComments
Don't add line breaks just to keep lines short when they are more readable long
(e.g., repetitive lines). Break lines because of what you're writing, not
because of line length.
Long lines often correlate with long names. If you find lines are too long,
consider whether the names could be shorter. Getting rid of long names often
helps more than wrapping lines.
This advice applies equally to function length—there's no rule "never have a
function more than N lines", but there is such a thing as too long. The solution
is to change where function boundaries are, not to count lines.
go
// Bad: Arbitrary mid-line break
func (s *Store) GetUser(ctx context.Context,
id string) (*User, error) {
// Good: All arguments on own lines
func (s *Store) GetUser(
ctx context.Context,
id string,
) (*User, error) {Go中没有严格的行长度限制,但应避免过长的行。Uber建议软限制为99个字符。
综合来源:Google + Uber + Go Wiki CodeReviewComments指南
准则:
- 如果一行看起来太长,应重构而非仅仅换行
- 不要在缩进变化前拆分(函数声明、条件语句)
- 不要将长字符串(如URL)拆分为多行
- 拆分时,将所有参数单独放在一行
- 如果已经尽可能短,可保留长行
按语义拆分,而非按长度:
建议:Go Wiki CodeReviewComments
不要仅仅为了缩短行而添加换行,长行有时可读性更强(如重复行)。应根据内容逻辑拆分,而非行长度。
长行通常与长名称相关。如果发现行太长,可考虑缩短名称。缩短名称往往比换行更有效。
此建议同样适用于函数长度——没有“函数永远不能超过N行”的规则,但确实存在过长的情况。解决方案是调整函数边界,而非统计行数。
go
// 不推荐:随意在中间换行
func (s *Store) GetUser(ctx context.Context,
id string) (*User, error) {
// 推荐:所有参数单独成行
func (s *Store) GetUser(
ctx context.Context,
id string,
) (*User, error) {Local Consistency
局部一致性
When the style guide is silent, be consistent with nearby code:
Valid local choices:
- vs
%sfor error formatting%v - Buffered channels vs mutexes
Invalid local overrides:
- Line length restrictions
- Assertion-based testing libraries
当风格指南未明确规定时,应与周边代码保持一致:
合理的局部选择:
- 错误格式化使用或
%s%v - 缓冲通道或互斥锁的选择
不允许的局部覆盖:
- 行长度限制
- 基于断言的测试库
Reduce Nesting
减少嵌套
Source: Uber Go Style Guide
Handle error cases and special conditions first. Return early or continue the loop to keep the "happy path" unindented.
go
// Bad: Deeply nested
for _, v := range data {
if v.F1 == 1 {
v = process(v)
if err := v.Call(); err == nil {
v.Send()
} else {
return err
}
} else {
log.Printf("Invalid v: %v", v)
}
}
// Good: Flat structure with early returns
for _, v := range data {
if v.F1 != 1 {
log.Printf("Invalid v: %v", v)
continue
}
v = process(v)
if err := v.Call(); err != nil {
return err
}
v.Send()
}来源:Uber Go风格指南
优先处理错误情况和特殊条件。提前返回或继续循环,让“正常流程”保持无缩进状态。
go
// 不推荐:深层嵌套
for _, v := range data {
if v.F1 == 1 {
v = process(v)
if err := v.Call(); err == nil {
v.Send()
} else {
return err
}
} else {
log.Printf("Invalid v: %v", v)
}
}
// 推荐:扁平结构,提前返回
for _, v := range data {
if v.F1 != 1 {
log.Printf("Invalid v: %v", v)
continue
}
v = process(v)
if err := v.Call(); err != nil {
return err
}
v.Send()
}Unnecessary Else
避免不必要的Else
Source: Uber Go Style Guide
If a variable is set in both branches of an if, use default + override pattern.
go
// Bad: Setting in both branches
var a int
if b {
a = 100
} else {
a = 10
}
// Good: Default + override
a := 10
if b {
a = 100
}来源:Uber Go风格指南
如果变量在if的两个分支中都被赋值,使用默认值+覆盖模式。
go
// 不推荐:两个分支都赋值
var a int
if b {
a = 100
} else {
a = 10
}
// 推荐:默认值+覆盖
a := 10
if b {
a = 100
}Naked Returns
裸返回
Advisory: Go Wiki CodeReviewComments
A statement without arguments returns the named return values. This is
known as a "naked" return.
returngo
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // returns x, y
}建议:Go Wiki CodeReviewComments
不带参数的语句会返回已命名的返回值,这被称为“裸返回”。
returngo
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // 返回x, y
}Guidelines for Naked Returns
裸返回使用准则
- OK in small functions: Naked returns are fine in functions that are just a handful of lines
- Be explicit in medium+ functions: Once a function grows to medium size, be explicit with return values for clarity
- Don't name results just for naked returns: Clarity of documentation is always more important than saving a line or two. Don't name result parameters just because it enables naked returns
go
// Good: Small function, naked return is clear
func minMax(a, b int) (min, max int) {
if a < b {
min, max = a, b
} else {
min, max = b, a
}
return
}
// Good: Larger function, explicit return
func processData(data []byte) (result []byte, err error) {
result = make([]byte, 0, len(data))
for _, b := range data {
if b == 0 {
return nil, errors.New("null byte in data")
}
result = append(result, transform(b))
}
return result, nil // explicit: clearer in longer functions
}See go-documentation for guidance on Named Result Parameters.
- 小型函数中可使用:裸返回在仅有几行的小型函数中是合适的
- 中型及以上函数需显式返回:函数增长到中型规模后,应显式指定返回值以保证清晰
- 不要为了裸返回而命名结果参数:文档清晰性永远比节省一两行代码更重要。不要仅仅为了使用裸返回而命名结果参数
go
// 推荐:小型函数,裸返回清晰明了
func minMax(a, b int) (min, max int) {
if a < b {
min, max = a, b
} else {
min, max = b, a
}
return
}
// 推荐:大型函数,显式返回
func processData(data []byte) (result []byte, err error) {
result = make([]byte, 0, len(data))
for _, b := range data {
if b == 0 {
return nil, errors.New("null byte in data")
}
result = append(result, transform(b))
}
return result, nil // 显式返回:在长函数中更清晰
}关于命名结果参数的指南,请参考go-documentation。
Quick Reference
快速参考
| Principle | Key Question |
|---|---|
| Clarity | Can a reader understand what and why? |
| Simplicity | Is this the simplest approach? |
| Concision | Is the signal-to-noise ratio high? |
| Maintainability | Can this be safely modified later? |
| Consistency | Does this match surrounding code? |
| 原则 | 核心问题 |
|---|---|
| 清晰性 | 阅读者能否理解代码的用途和设计思路? |
| 简洁性 | 这是最简单的实现方式吗? |
| 高信噪比 | 代码的信噪比是否足够高? |
| 可维护性 | 后续能否安全地修改这段代码? |
| 一致性 | 这段代码是否与周边代码保持一致? |
See Also
相关参考
- For naming conventions:
go-naming - For error handling patterns:
go-error-handling - For documentation guidelines:
go-documentation - For testing best practices:
go-testing - For defensive programming:
go-defensive - For performance optimization:
go-performance - For linting and static analysis:
go-linting
- 命名规范:
go-naming - 错误处理模式:
go-error-handling - 文档指南:
go-documentation - 测试最佳实践:
go-testing - 防御式编程:
go-defensive - 性能优化:
go-performance - 代码检查与静态分析:
go-linting