| name | go-style-core |
| description | Use when working with Go formatting, line length, nesting, naked returns, semicolons, or core style principles. Also use when a style question isn't covered by a more specific skill, even if the user doesn't reference a specific style rule. Does not cover domain-specific patterns like error handling, naming, or testing (see specialized skills). Acts as fallback when no more specific style skill applies. |
| license | Apache-2.0 |
| metadata | {"sources":"Effective Go, Google Style Guide, Uber Style Guide, Go Wiki CodeReviewComments"} |
Go 风格核心原则
风格原则(优先级顺序)
编写可读 Go 代码时,按以下重要性顺序应用这些原则:
优先级顺序
- 清晰性 — 读者能否在没有额外上下文的情况下理解代码?
- 简洁性 — 这是否是实现目标的最简单方式?
- 精炼性 — 每一行是否都有其存在的价值?
- 可维护性 — 后续修改是否容易?
- 一致性 — 是否与周围代码和项目约定保持一致?
在解决清晰性、简洁性和精炼性之间的冲突时,或需要具体示例了解每个原则在实际 Go 代码中的应用时,请阅读 references/PRINCIPLES.md。
格式化
运行 gofmt — 没有例外。没有严格的行长度限制,但 Uber 建议软限制为 99 个字符。按语义换行,而非按长度 — 选择重构而非仅仅换行。
在配置 gofmt、决定换行策略、应用 MixedCaps 规则或解决局部一致性问题时,请阅读 references/FORMATTING.md。
减少嵌套
优先处理错误情况和特殊条件。提前返回或继续循环,使"正常路径"保持无缩进。
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()
}
不必要的 Else
如果变量在 if 的两个分支中都被赋值,使用默认值 + 覆盖模式。
var a int
if b {
a = 100
} else {
a = 10
}
a := 10
if b {
a = 100
}
裸返回
没有参数的 return 语句会返回命名返回值。这被称为"裸"返回。
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
裸返回的使用指南
- 在小型函数中可以使用:裸返回在只有几行的函数中是没问题的
- 在中大型函数中要明确:一旦函数增长到中等大小,为了清晰起见应明确指定返回值
- 不要仅为了裸返回而命名返回值:文档的清晰性始终比节省一两行更重要
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。
分号
Go 的词法分析器会在任何最后一个 token 是标识符、字面量或以下关键字之一的行后自动插入分号:break continue fallthrough return ++ -- ) }。
这意味着 左花括号必须与控制结构在同一行:
if i < f() {
g()
}
if i < f()
{
g()
}
在惯用 Go 中,显式分号仅出现在 for 循环子句中和用于分隔单行上的多个语句。
快速参考
| 原则 | 核心问题 |
|---|
| 清晰性 | 读者能否理解代码的意图和原因? |
| 简洁性 | 这是否是最简单的方法? |
| 精炼性 | 信噪比是否高? |
| 可维护性 | 后续能否安全地修改? |
| 一致性 | 是否与周围代码保持一致? |
相关 Skill