如何在Go中编写注释
- 创业
- 2025-08-18 13:03:02

引言
几乎所有的编程语言都有一种向代码添加注释的语法,Go也不例外。注释(comment)是程序中使用人类语言解释代码如何工作或为什么要这样写的行。编译器会忽略它们,但细心的程序员不会。注释添加了宝贵的上下文,可以帮助您的合作者(以及您未来的自己)避免陷阱并编写更可维护的代码。
任何包中的普通注释都解释了该代码为什么做它所做的事情。它们是针对包开发人员的注意事项和警告。文档注释总结了包中每个组件的功能以及工作原理,并提供了示例代码和命令用法。它们是用户的官方包文档。
在本文中,我们将从几个Go包中查看一些真实的注释,不仅说明注释在Go中是什么样子的,还说明它们应该传达什么。
普通的评论Go中的注释以两个斜杠(//)开始,然后是一个空格(不是必需的,但习惯用法),然后是注释。它可能出现在所涉及代码的上方或右侧。在上面,它会缩进以与代码对齐。
这个Hello World程序在它自己的一行中只包含一个注释:
hello.go
package main import "fmt" func main() { // 通过控制台打招呼 fmt.Println("Hello, World!") }**注意:**如果你添加了与代码不一致的注释,gofmt工具会解决这个问题。该工具随您的Go安装一起提供,将Go代码(包括注释)格式化为通用格式,以便任何地方的Go代码看起来都是相同的,程序员不会因为制表符和空格而争论。作为一名Gopher (Go爱好者的称呼),您应该在编写Go代码时不断格式化代码,并且在将其提交到版本控制系统之前。你可以手动运行gofmt (gofmt -w hello.go),但更方便的是配置你的文本编辑器或IDE,使其在每次保存文件时运行。
由于这段注释很短,它可以作为行内注释出现在代码的右侧:
hello.go
. . . fmt.Println("Hello, World!") // 通过控制台打招呼 . . .大多数注释都单独出现在一行中,除非它们非常简短。
较长的注释跨越多行。Go支持c风格的块注释,使用/*和*/标签来打开和关闭非常长的注释,但这些仅用于特殊情况。(稍后会详细介绍。)普通的多行注释以//开头,而不是使用块注释标签。
下面是一些带有许多注释的代码,每个注释都正确缩进。其中一个多行注释被突出显示:
color.go
package main import "fmt" const favColor string = "blue" // Could have chosen any color func main() { var guess string // Create an input loop for { // Ask the user to guess my favorite color fmt.Println("Guess my favorite color:") // Try to read a line of input from the user. // Print out an error and exit, if there is one. if _, err := fmt.Scanln(&guess); err != nil { fmt.Printf("%s\n", err) return } // Did they guess the correct color? if favColor == guess { // They guessed it! fmt.Printf("%q is my favorite color!\n", favColor) return } // Wrong! Have them guess again. fmt.Printf("Sorry, %q is not my favorite color. Guess again.\n", guess) } }这些注释中的大多数实际上都是混乱的。这么小而简单的程序不应该包含这么多注释,而且其中大多数注释的含义在代码本身就很明显。您可以相信其他Go程序员能够理解Go语法、控制流、数据类型等基础知识。你不需要写注释来宣布代码将要遍历一个切片或将两个浮点数相乘。
然而,其中有一条注释是有用的。
好的评论可以解释为什么在任何程序中,最有用的注释都不是解释代码做了什么或如何做,而是解释为什么这样做。有时没有为什么,即使这样也可以指出来,就像下面这段行内注释所做的那样:
color.go
const favColor string = "blue" // 可以选择任何颜色吗这段注释说明了代码中没有的东西:值“blue”是程序员任意选择的。换句话说,//可以随意更改。
然而,大多数代码都有一个为什么。这是Go标准库中的net/http包中的一个函数,其中包含两个非常有用的注释:
client.go
. . . // refererForURL returns a referer without any authentication info or // an empty string if lastReq scheme is https and newReq scheme is http. func refererForURL(lastReq, newReq *url.URL) string { // tools.ietf.org/html/rfc7231#section-5.5.2 // "Clients SHOULD NOT include a Referer header field in a // (non-secure) HTTP request if the referring page was // transferred with a secure protocol." if lastReq.Scheme == "https" && newReq.Scheme == "http" { return "" } referer := lastReq.String() if lastReq.User != nil { // This is not very efficient, but is the best we can // do without: // - introducing a new method on URL // - creating a race condition // - copying the URL struct manually, which would cause // maintenance problems down the line auth := lastReq.User.String() + "@" referer = strings.Replace(referer, auth, "", 1) } return referer } . . .第一个突出显示的注释警告维护者不要更改下面的代码,因为它是为了符合HTTP协议的RFC(官方规范)而编写的。第二个高亮的注释承认下面的代码并不理想,暗示了维护者可能会如何尝试改进它,并警告他们这样做的危险。
这样的注释是必不可少的。它们防止维护者在不知情的情况下引入bug和其他问题,同时也可能邀请他们实现新的想法,但要谨慎。
func声明上面的注释也很有用,但方式不同。让我们接下来探讨这种评论。
文档注释出现在顶级(非缩进)声明之上的注释,如package、func、const、var和type,被称为文档注释。之所以这样命名,是因为它们代表了包及其所有导出名称的官方文档。
注意:在Go中,exported的意思与public在某些语言中的意思相同:导出的组件是其他包在导入你的包时可能会使用的组件。要导出包中的任何顶级名称,只需将其大写即可。
文档注释解释了做什么和怎么做与我们刚才看到的普通注释不同,文档注释通常会解释代码做什么或如何做。这是因为它们不是为包的维护者准备的,而是为它的用户准备的,这些用户通常不想阅读或贡献代码。
用户通常会在以下三个地方阅读文档注释:
在他们的本地终端中,通过在单个源文件或目录上运行go doc。
在pkg.go.dev上,任何公开的Go包的官方文档中心。
在您的团队使用godoc工具托管的私人运行的web服务器上。此工具可让您的团队为私有Go包创建自己的参考门户。
在开发Go包时,您应该为每个导出的名称编写文档注释。(CodeReviewComments。)这是godo (DigitalOcean API的Go客户端库)中的一行文档注释:
godo.go
// Client manages communication with DigitalOcean V2 API. type Client struct {像这样简单的文档注释似乎没有必要,但请记住,它将与所有其他文档注释一起出现,以全面记录包的每个可用组件。
下面是这个包的一段较长的文档注释:
godo.go
// Do sends an API request and returns the API response. The API response is JSON decoded and stored in the value // pointed to by v, or returned as an error if an API error has occurred. If v implements the io.Writer interface, // the raw response will be written to v, without attempting to decode it. func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) { . . . }函数的文档注释应该清楚地指定预期的参数格式(如果不明显的话)和函数返回数据的格式。他们还可以总结该函数的工作原理。
将Do函数的文档注释与函数内部的注释进行比较:
godo.go
// Ensure the response body is fully read and closed // before we reconnect, so that we reuse the same TCPConnection. // Close the previous response's body. But read at least some of // the body so if it's small the underlying TCP connection will be // re-used. No need to check for errors: if it fails, the Transport // won't reuse it anyway.这就像我们在net/http包中看到的评论。阅读这段代码的维护者可能会想,“为什么这段代码不检查错误呢?,然后添加错误检查。但是这条评论解释了为什么他们不需要这样做。它不像文档注释那样是高级的** what 或 how **。
最高级别的文档注释是包注释。每个包都应该只包含一个包注释,概述包是什么以及如何使用它,包括代码和/或命令示例。包注释可以出现在任何源文件中,而且只能出现在package <name>声明之上的那个文件中。包注释通常出现在名为doc.go的单独文件中。
与我们看过的所有其他注释不同,包注释通常使用/*和*/,因为它们可能很长。以下是gofmt包注释的开头:
/* Gofmt formats Go programs. It uses tabs for indentation and blanks for alignment. Alignment assumes that an editor is using a fixed-width font. Without an explicit path, it processes the standard input. Given a file, it operates on that file; given a directory, it operates on all .go files in that directory, recursively. (Files starting with a period are ignored.) By default, gofmt prints the reformatted sources to standard output. Usage: gofmt [flags] [path ... The flags are: -d Do not print reformatted sources to standard output. If a file's formatting is different than gofmt's, print diffs to standard output. . . . */ package main那么文档注释的格式如何呢?他们可以(或必须)拥有什么样的结构?
文档注释有自己的格式根据Go开发者的一篇旧博客文章:
Godoc在概念上与Python的文档字符串和Java的Javadoc有关,但它的设计更简单。godoc读取的注释不是语言结构(像Docstring那样),也必须有自己的机器可读的语法(像Javadoc那样)。Godoc注释只是好的注释,即使Godoc不存在,你也会想要阅读的那种注释。
虽然文档注释没有必须的格式,但它们可以选择使用Go文档中完全描述的“Markdown的简化子集”格式。在文档注释中,要以段落和列表的形式书写,以缩进的形式显示示例代码或命令,提供引用的链接等。当文档注释按照这种格式结构良好时,它们就可以呈现为漂亮的网页。
以下是添加到[如何用Go编写第一个程序]中的扩展Hello World程序greeting.go中的一些注释。
greeting.go
// This is a doc comment for greeting.go. // - prompt user for name. // - wait for name // - print name. // This is the second paragraph of this doc comment. // `gofmt` (and `go doc`) will insert a blank line before it. package main import ( "fmt" "strings" ) func main() { // This is not a doc comment. Gofmt will NOT format it. // - prompt user for name // - wait for name // - print name // This is not a "second paragraph" because this is not a doc comment. // It's just more lines to this non-doc comment. fmt.Println("Please enter your name.") var name string fmt.Scanln(&name) name = strings.TrimSpace(name) fmt.Printf("Hi, %s! I'm Go!", name) }package main上面的注释是一个文档注释。它试图使用文档注释格式的段落和列表的概念,但并不完全正确。gofmt工具将把它塑造成这种格式。运行gofmt greeting.go将打印以下内容:
// This is a doc comment for greeting.go. // - prompt user for name. // - wait for name. // - print name. // // This is the second paragraph of this doc comment. // `gofmt` (and `go doc`) will insert a blank line before it. package main import ( "fmt" "strings" ) func main() { // This is not a doc comment. `gofmt` will NOT format it. // - prompt user for name // - wait for name // - print name // This is not a "second paragraph" because this is not a doc comment. // It's just more lines to this non-doc comment. fmt.Println("Please enter your name.") var name string fmt.Scanln(&name) name = strings.TrimSpace(name) fmt.Printf("Hi, %s! I'm Go!", name) }注意:
文档注释第一段中列出的项目现在对齐了。
第一段和第二段之间现在有一个空行。
3.main()中的注释没有被格式化,因为gofmt识别出它不是文档注释。(但如前所述,gofmt会将所有注释对齐到与代码相同的缩进。)
运行go doc greeting.go将格式化并打印文档注释,但不是main()中的文档注释:
This is a doc comment for greeting.go. - prompt user for name. - wait for name. - print name. This is the second paragraph of this doc comment. `gofmt` (and `go doc`) will insert a blank line before it.如果你始终正确地使用这种文档注释格式,包的用户会感谢你提供了易于阅读的文档。
阅读文档注释上的官方参考页面来学习如何写好它们的一切。
快速禁用代码你是否曾经写过一些新代码,让你的应用程序变慢,甚至更糟,破坏了一切?另一种情况是使用c风格的/*和*/标签。你可以通过在代码前面加一个/*、后面加一个*/来快速禁用有问题的代码。用这些标签把有问题的代码包裹起来,把它变成一个块注释。然后,当您修复了它导致的任何问题后,您可以通过删除这两个标签重新启用代码。
problematic.go
. . . func main() { x := initializeStuff() /* This code is causing problems, so we're going to comment it out for now someProblematicCode(x) */ fmt.Println("This code is still running!") }对于较长的代码块,使用这些标签比在每行有问题的代码的开头添加//要方便得多。作为一种约定,使用//表示普通注释和文档注释,它们将无限期地存在于代码中。仅在测试期间临时使用/*和*/标签(或如前所述用于包注释)。不要在程序中长时间地留下注释代码片段。
总结通过在你所有的Go程序中编写富有表现力的注释,你可以:
防止你的合作者破坏东西。
帮助未来的自己,他有时已经忘记代码最初为什么要这样写。
给包的用户一个参考,他们可以在不深入代码的情况下阅读。
如何在Go中编写注释由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“如何在Go中编写注释”
上一篇
MySQL之DML
下一篇
RocketMQ—消费者负载均衡