Files
OpenFlare/.agents/skills/go-style-core/SKILL.md
T
2026-08-04 11:40:32 +08:00

180 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 代码时,按以下重要性顺序应用这些原则:
### 优先级顺序
1. **清晰性** — 读者能否在没有额外上下文的情况下理解代码?
2. **简洁性** — 这是否是实现目标的最简单方式?
3. **精炼性** — 每一行是否都有其存在的价值?
4. **可维护性** — 后续修改是否容易?
5. **一致性** — 是否与周围代码和项目约定保持一致?
> 在解决清晰性、简洁性和精炼性之间的冲突时,或需要具体示例了解每个原则在实际 Go 代码中的应用时,请阅读 [references/PRINCIPLES.md](references/PRINCIPLES.md)。
---
## 格式化
运行 `gofmt` — 没有例外。**没有严格的行长度限制**,但 Uber 建议软限制为 99 个字符。按语义换行,而非按长度 — 选择重构而非仅仅换行。
> 在配置 gofmt、决定换行策略、应用 MixedCaps 规则或解决局部一致性问题时,请阅读 [references/FORMATTING.md](references/FORMATTING.md)。
---
## 减少嵌套
优先处理错误情况和特殊条件。提前返回或继续循环,使"正常路径"保持无缩进。
```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()
}
```
### 不必要的 Else
如果变量在 if 的两个分支中都被赋值,使用默认值 + 覆盖模式。
```go
// 不好:在两个分支中都赋值
var a int
if b {
a = 100
} else {
a = 10
}
// 好:默认值 + 覆盖
a := 10
if b {
a = 100
}
```
---
## 裸返回
没有参数的 `return` 语句会返回命名返回值。这被称为"裸"返回。
```go
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // 返回 x, y
}
```
### 裸返回的使用指南
- **在小型函数中可以使用**:裸返回在只有几行的函数中是没问题的
- **在中大型函数中要明确**:一旦函数增长到中等大小,为了清晰起见应明确指定返回值
- **不要仅为了裸返回而命名返回值**:文档的清晰性始终比节省一两行更重要
```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**。
---
## 分号
Go 的词法分析器会在任何最后一个 token 是标识符、字面量或以下关键字之一的行后自动插入分号:`break continue fallthrough return ++ -- ) }`。
这意味着 **左花括号必须与控制结构在同一行**:
```go
// 好:花括号在同一行
if i < f() {
g()
}
// 不好:花括号在下一行 — 词法分析器会在 f() 后插入分号
if i < f() // 错误!
{ // 错误!
g()
}
```
在惯用 Go 中,显式分号仅出现在 `for` 循环子句中和用于分隔单行上的多个语句。
---
## 快速参考
| 原则 | 核心问题 |
|------|----------|
| 清晰性 | 读者能否理解代码的意图和原因? |
| 简洁性 | 这是否是最简单的方法? |
| 精炼性 | 信噪比是否高? |
| 可维护性 | 后续能否安全地修改? |
| 一致性 | 是否与周围代码保持一致? |
## 相关 Skill
- **命名约定**:在应用 MixedCaps、选择标识符名称或解决命名争议时,请参阅 [go-naming](../go-naming/SKILL.md)
- **错误流程**:在构建错误优先的守卫子句或通过提前返回减少嵌套时,请参阅 [go-error-handling](../go-error-handling/SKILL.md)
- **文档**:在编写文档注释、命名返回参数或包级别文档时,请参阅 [go-documentation](../go-documentation/SKILL.md)
- **Linting 执行**:在使用 golangci-lint 自动化风格检查或配置 CI 时,请参阅 [go-linting](../go-linting/SKILL.md)
- **代码审查**:在系统性代码审查中应用风格原则时,请参阅 [go-code-review](../go-code-review/SKILL.md)
- **日志风格**:在审查日志实践、在 log 和 slog 之间选择或组织日志输出时,请参阅 [go-logging](../go-logging/SKILL.md)