mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-12 10:16:37 +08:00
wavelet init
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
---
|
||||
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)
|
||||
@@ -0,0 +1,95 @@
|
||||
# 格式化参考
|
||||
|
||||
## gofmt 是必须的
|
||||
|
||||
所有 Go 源文件 **必须** 符合 `gofmt` 的输出。没有例外。
|
||||
|
||||
```bash
|
||||
# 格式化一个文件
|
||||
gofmt -w myfile.go
|
||||
|
||||
# 格式化目录下所有文件
|
||||
gofmt -w .
|
||||
```
|
||||
|
||||
其他格式化工具:
|
||||
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `gofmt` | 标准格式化工具(必须使用) |
|
||||
| `goimports` | gofmt + import 管理 |
|
||||
| `gofumpt` | gofmt 的更严格超集 |
|
||||
|
||||
---
|
||||
|
||||
## 括号
|
||||
|
||||
Go 比 C 和 Java 需要更少的括号。控制结构(`if`、`for`、`switch`)的语法中不需要括号。运算符优先级层次更短更清晰,所以 `x<<8 + y<<16` 的含义就如空格所暗示的那样 — 不像其他语言。
|
||||
|
||||
---
|
||||
|
||||
## MixedCaps(驼峰命名)
|
||||
|
||||
Go 使用 `MixedCaps` 或 `mixedCaps`,从不使用下划线:
|
||||
|
||||
```go
|
||||
// 好
|
||||
MaxLength // 导出常量
|
||||
maxLength // 非导出常量
|
||||
userID // 变量
|
||||
|
||||
// 不好
|
||||
MAX_LENGTH // 不使用 snake_case
|
||||
max_length // 不使用下划线
|
||||
```
|
||||
|
||||
例外:
|
||||
- 测试函数名可以使用下划线:`TestFoo_Bar`
|
||||
- 与 OS/cgo 交互的生成代码
|
||||
|
||||
---
|
||||
|
||||
## 行长度
|
||||
|
||||
Go 中 **没有严格的行长度限制**,但避免过长的行。Uber 建议软限制为 99 个字符。
|
||||
|
||||
指导原则:
|
||||
- 如果一行感觉太长,**重构** 而非仅仅换行
|
||||
- 不要在缩进变化之前换行(函数声明、条件语句)
|
||||
- 不要将长字符串(URL)拆分成多行
|
||||
- 换行时,将所有参数放在各自的行上
|
||||
- 如果已经尽可能短了,就让它保持长行
|
||||
|
||||
**按语义换行,而非按长度**:
|
||||
|
||||
不要仅仅为了保持短行而添加换行符,当长行更具可读性时(例如,重复性的行)。因为你所写的内容而换行,而非因为行长度。
|
||||
|
||||
长行通常与长名称相关。如果你发现行太长,考虑名称是否可以更短。去掉长名称往往比换行更有帮助。
|
||||
|
||||
这个建议同样适用于函数长度 — 没有"函数永远不超过 N 行"的规则,但确实存在太长的情况。解决方案是改变函数的边界在哪里,而非计算行数。
|
||||
|
||||
```go
|
||||
// 不好:随意的行中断
|
||||
func (s *Store) GetUser(ctx context.Context,
|
||||
id string) (*User, error) {
|
||||
|
||||
// 好:所有参数各占一行
|
||||
func (s *Store) GetUser(
|
||||
ctx context.Context,
|
||||
id string,
|
||||
) (*User, error) {
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 局部一致性
|
||||
|
||||
当风格指南未做规定时,与附近代码保持一致:
|
||||
|
||||
**有效的** 局部选择:
|
||||
- 错误格式化使用 `%s` 还是 `%v`
|
||||
- 带缓冲 channel 还是 mutex
|
||||
|
||||
**无效的** 局部覆盖:
|
||||
- 行长度限制
|
||||
- 基于断言的测试库
|
||||
@@ -0,0 +1,89 @@
|
||||
# 风格原则参考
|
||||
|
||||
## 1. 清晰性
|
||||
|
||||
代码的目的和原理必须对读者清晰。
|
||||
|
||||
- **做什么**:使用描述性名称、有帮助的注释和高效的组织
|
||||
- **为什么**:添加解释原理的注释,特别是对于微妙的细节
|
||||
- 从读者的角度审视清晰性,而非作者的角度
|
||||
- 代码应该易于阅读,而非易于编写
|
||||
|
||||
```go
|
||||
// 好:目的清晰
|
||||
func (c *Config) WriteTo(w io.Writer) (int64, error)
|
||||
|
||||
// 不好:不清晰,重复了接收者
|
||||
func (c *Config) WriteConfigTo(w io.Writer) (int64, error)
|
||||
```
|
||||
|
||||
## 2. 简洁性
|
||||
|
||||
代码应该以最简单的方式实现目标。
|
||||
|
||||
简洁的代码:
|
||||
- 从头到尾容易阅读
|
||||
- 不假定读者有先验知识
|
||||
- 没有不必要的抽象层次
|
||||
- 注释解释"为什么",而非"做什么"
|
||||
- 可能与"巧妙"的代码互斥
|
||||
|
||||
### 最少机制
|
||||
|
||||
当有几种方式表达同一个想法时,优先使用最标准的工具:
|
||||
|
||||
1. 核心语言结构(channel、slice、map、loop、struct)
|
||||
2. 标准库(HTTP 客户端、模板引擎)
|
||||
3. 第三方库 — 仅在 (1) 和 (2) 不够用时使用
|
||||
|
||||
## 3. 精炼性
|
||||
|
||||
代码应该有高信噪比。
|
||||
|
||||
- 避免重复代码
|
||||
- 避免多余的语法
|
||||
- 避免不必要的抽象
|
||||
- 使用表驱动测试提取公共代码
|
||||
|
||||
```go
|
||||
// 好:常见惯用法,信号量高
|
||||
if err := doSomething(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 好:为异常情况增强信号
|
||||
if err := doSomething(); err == nil { // 如果没有错误
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 可维护性
|
||||
|
||||
代码被修改的次数远多于被编写的次数。
|
||||
|
||||
可维护的代码:
|
||||
- 对于未来的程序员来说容易正确修改
|
||||
- API 能够优雅地扩展
|
||||
- 使用可预测的名称(相同概念 = 相同名称)
|
||||
- 最小化依赖
|
||||
- 具有全面的测试和清晰的诊断信息
|
||||
|
||||
```go
|
||||
// 不好:关键细节被隐藏
|
||||
if user, err = db.UserByID(userID); err != nil { // = vs :=
|
||||
|
||||
// 好:显式且清晰
|
||||
u, err := db.UserByID(userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid origin user: %s", err)
|
||||
}
|
||||
user = u
|
||||
```
|
||||
|
||||
## 5. 一致性
|
||||
|
||||
代码的外观和行为应该与代码库中的类似代码一致。
|
||||
|
||||
- 包级别的一致性最重要
|
||||
- 当出现平局时,优先保持一致性
|
||||
- 绝不为了局部一致性而覆盖有文档记录的风格原则
|
||||
Reference in New Issue
Block a user