跳到主要内容

Comment(注释)

Go 支持 // 行注释和 /* ... */ 块注释。除了说明实现原因,注释还承担生成包文档、标记 API 弃用状态和承载工具链指令等职责,因此位置和格式有时会影响程序之外的工具行为。

两种注释

package main

import "fmt"

func main() {
// 行注释从 // 开始,到当前行结束。
message := "Hello, Go"

/* 块注释可以写在一行,
也可以跨越多行。 */
fmt.Println(message)
}

块注释不能嵌套。注释标记出现在解释字符串或 rune 字面量内部时,只是普通字符:

package main

import "fmt"

func main() {
fmt.Println("https://go.dev") // 字符串中的 // 不会开始注释
}

短说明通常使用连续的 //,这样更容易增删某一行,也符合 Go 源码中的常见习惯。块注释适合暂时保留较长说明或嵌入式示例,但不应用于长期“注释掉”失效代码;版本控制已经保存历史。

文档注释

紧邻包、常量、变量、函数、类型、字段或方法声明之前的注释可以成为文档注释(doc comment),由 go doc 等 Go 工具读取:

// Package temperature 提供摄氏温度与华氏温度之间的转换。
package temperature

// Celsius 表示摄氏温度。
type Celsius float64

// Fahrenheit 将摄氏温度转换为华氏温度。
func (c Celsius) Fahrenheit() float64 {
return float64(c)*9/5 + 32
}

好的文档注释应遵循以下习惯:

  • 包注释以 Package 包名 开头,说明包解决什么问题。
  • 导出声明的注释以被声明的名称开头,并形成完整句子。
  • 说明调用约束、错误、并发安全或副作用,而不是重复函数签名。
  • 注释与声明之间不要插入无关声明,以免文档工具无法正确关联。

在模块目录运行以下命令可以查看生成效果:

go doc ./...

具体格式以 Go Doc Comments 为准;工具会按段落、标题、列表、代码块和链接等规则解释注释文本。

并非每个局部变量都需要注释。命名和结构已经能清楚表达“做什么”时,注释应重点解释“为什么这样做”、有哪些非显然约束,以及哪些行为是调用方必须知道的。

包注释

包注释写在 package 子句之前。较短的包说明可以放在任意一个非测试文件中;较长的说明常单独放在 doc.go

// Package retry 提供带退避策略的有限次数重试。
//
// 调用方应确保传入操作可以安全重试,并为整个重试过程设置截止时间。
package retry

同一个包通常维护一份主要包注释,避免不同文件生成重复或矛盾的概述。

弃用说明

公开 API 不再推荐使用时,可以在文档注释的独立段落中使用 Deprecated: 标记:

// ParseAddress 解析服务地址。
//
// Deprecated: Use ParseEndpoint instead.
func ParseAddress(value string) string {
return value
}

弃用说明应指出替代方案和必要的迁移条件。Deprecated: 是工具可识别的约定,不要用“旧方法”“暂勿使用”等模糊措辞代替。

工具链指令不是普通说明

//go: 开头的注释可能是编译器或工具链指令,例如生成命令:

package assets

//go:generate go run ./cmd/generate

这类指令对位置和空格有严格要求,只有执行相应工具时才产生效果。例如 go generate ./... 会处理 //go:generate,普通构建不会自动运行生成命令。

不要为了视觉统一修改未知的 //go: 指令,也不要在普通说明中随意使用此前缀。修改前应查询对应工具的正式文档。

注释应记录什么

适合写入注释的内容包括:

  • 业务规则背后的原因和外部约束。
  • 看似可以简化、实际必须保留的兼容处理。
  • 公开 API 的错误、资源所有权和并发安全约定。
  • 临时方案的移除条件,并配合可追踪的任务编号。

下面的注释只是在重复代码,应优先删除:

// count 加一。
count++

下面的注释解释了非显然原因,具有维护价值:

// 上游按自然日结算,因此这里必须使用业务时区,不能直接按 UTC 截断。
settlementDate := now.In(businessLocation).Format("2006-01-02")

与 TypeScript 的区别

两种语言都支持 ///* ... */,也都能从注释生成 API 文档。不过 Go 的文档工具偏好紧邻声明、以声明名称开头的自然语言注释;Go 还使用 //go: 承载工具链指令。不要把 JSDoc 的 @param@returns 模板机械搬到 Go,参数与返回类型已经由函数签名表达。

小结

  • // 用于行注释,/* ... */ 用于块注释,块注释不能嵌套。
  • 文档注释应紧邻声明,导出名称的说明通常以该名称开头。
  • 包注释说明整个包的职责,较长内容可以放在 doc.go
  • Deprecated: 是工具可识别的弃用标记,应同时给出替代方案。
  • //go: 可能影响工具链行为,必须按对应指令的正式规则维护。