介绍
几乎所有的编程语言都有添加评论的语法,而Go也不例外。评论是程序中的行,在人类语言中解释代码是如何工作的,或者为什么它是写的。
任何包中的常见评论解释了为什么该代码会做它所做的。它们是对一个包的开发人员的注释和警告。 Doc comments 概括了包的每个组件在做什么以及它是如何工作的,提供示例代码和命令使用。
在本文中,我们将看看一些Go包的实际评论,以说明不仅评论在Go中看起来如何,而且它们应该传达什么。
常见评论
Go 中的评论以两个前进切片(//
)开始,然后是一个空间(不需要,但语言),然后是评论. 它可能出现在它所涉及的代码的右上方或右上方。
这个Hello World节目包含一个单独的评论:
1[label hello.go]
2package main
3
4import "fmt"
5
6func main() {
7 // Say hi via the console
8 fmt.Println("Hello, World!")
9}
<$>[note]
**注:**如果您添加了不与代码一致的评论,则 gofmt
工具会修复此问题。此工具与您的 Go 安装一起,将 Go 代码(包括评论)格式化为通用格式,使 Go 代码到处都看起来相同,而程序员也无法争论 tabs 和 spaces。作为 Gopher(如 Go 爱好者所称),您应该在编写时不间断地格式化 Go 代码,也许在将其交付到版本控制之前。您可以手动运行 gofmt (gofmt -wmt hello.go
),但更方便的是配置您的文本编辑器或 IDE 每次保存文件
由于这个评论如此短,它可以作为代码右侧的内线评论显示:
1[label hello.go]
2. . .
3 fmt.Println("Hello, World!") // Say hi via the console
4. . .
大多数评论都出现在自己的行上,除非它们很短,像这个。
更长的评论覆盖多个行. Go 支持 C 风格 block comments 使用 /*
和 */
标签来打开和关闭非常长的评论,但这些只用于特殊情况下。
这里有一些代码,有许多评论,每个评论都正确地注入。
1[label color.go]
2package main
3
4import "fmt"
5
6const favColor string = "blue" // Could have chosen any color
7
8func main() {
9 var guess string
10 // Create an input loop
11 for {
12 // Ask the user to guess my favorite color
13 fmt.Println("Guess my favorite color:")
14
15 // Try to read a line of input from the user.
16 // Print out an error and exit, if there is one.
17 if _, err := fmt.Scanln(&guess); err != nil {
18 fmt.Printf("%s\n", err)
19 return
20 }
21
22 // Did they guess the correct color?
23 if favColor == guess {
24 // They guessed it!
25 fmt.Printf("%q is my favorite color!\n", favColor)
26 return
27 }
28 // Wrong! Have them guess again.
29 fmt.Printf("Sorry, %q is not my favorite color. Guess again.\n", guess)
30 }
31}
大多数这些评论实际上都是混乱的。这样一个小而简单的程序不应该包含这么多的评论,而这些评论大多是代码本身显而易见的。你可以信任其他Go程序员来理解Go语法,控制流,数据类型等基本知识。
然而,其中一个评论是有用的。
好评论 解释为什么
任何程序中最有用的评论不是解释代码做什么或它如何做到的,而是为什么它做到了。
1[label color.go]
2const favColor string = "blue" // Could have chosen any color
这个评论说了一些代码不能做的事情:这个值是由程序员任意选择的蓝色
。
然而,大多数代码确实有 why. 这里是 Go 标准库中的 net/http
包中的一个函数,其中包含两个非常有用的评论:
1[label client.go]
2. . .
3// refererForURL returns a referer without any authentication info or
4// an empty string if lastReq scheme is https and newReq scheme is http.
5func refererForURL(lastReq, newReq *url.URL) string {
6 // https://tools.ietf.org/html/rfc7231#section-5.5.2
7 // "Clients SHOULD NOT include a Referer header field in a
8 // (non-secure) HTTP request if the referring page was
9 // transferred with a secure protocol."
10 if lastReq.Scheme == "https" && newReq.Scheme == "http" {
11 return ""
12 }
13 referer := lastReq.String()
14 if lastReq.User != nil {
15 // This is not very efficient, but is the best we can
16 // do without:
17 // - introducing a new method on URL
18 // - creating a race condition
19 // - copying the URL struct manually, which would cause
20 // maintenance problems down the line
21 auth := lastReq.User.String() + "@"
22 referer = strings.Replace(referer, auth, "", 1)
23 }
24 return referer
25}
26. . .
第一个突出评论警告维护者不要更改下面的代码,因为它是故意写的,以符合HTTP协议的RFC(官方规格)。
这些评论是不可或缺的,它们阻止维护人员无意中引入错误和其他问题,但也可能邀请他们谨慎地实施新想法。
上面的func
声明的评论也是有用的,但以不同的方式。
博士评论
如包
、func
、const
、var
和type
等顶级(非标注)声明的评论被称为doc评论
。
<$>[注] 注:在Go中, _exported_在某些语言中意味着相同的事情:一个导出组件是其他包在导入您的包时可能使用的组件。
Doc 评论 解释 What 和 How
与我们刚刚看到的普通评论不同,doc评论通常解释了代码的作用或它如何做到这一点,因为它们不是针对包的维护者,而是针对用户,他们通常不希望阅读或贡献代码。
用户通常会在三个地方读到您的doc评论:
- 在本地终端中,在单独的源文件或目录上运行
go doc
- 在 pkg.go.dev,任何公共 Go 包的官方文件中心
- 在私人运营的 Web 服务器上,由您的团队使用
godoc
工具托管。
在开发 Go 包时,您应该为每个导出的名称写一份 doc 评论(偶尔对于未导出的名称)(https://github.com/golang/go/wiki/CodeReviewComments#doc-comments)。
1[label godo.go]
2// Client manages communication with DigitalOcean V2 API.
3type Client struct {
像这样的简单的文档评论可能看起来不必要,但请记住,它会出现在你的所有其他文档评论旁边,以全面地记录包的每个可用的组件。
以下是该包的更长的doc评论:
1[label godo.go]
2// Do sends an API request and returns the API response. The API response is JSON decoded and stored in the value
3// pointed to by v, or returned as an error if an API error has occurred. If v implements the io.Writer interface,
4// the raw response will be written to v, without attempting to decode it.
5func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) {
6. . .
7}
函数的文档评论应该清楚地指定参数的预期格式(当它不明显时)和函数返回的数据的格式。
将做
函数的 doc 评论与函数中的此评论进行比较:
1[label godo.go]
2 // Ensure the response body is fully read and closed
3 // before we reconnect, so that we reuse the same TCPConnection.
4 // Close the previous response's body. But read at least some of
5 // the body so if it's small the underlying TCP connection will be
6 // re-used. No need to check for errors: if it fails, the Transport
7 // won't reuse it anyway.
这就像我们在net/http
包中看到的评论一样。阅读本评论下面的代码的维护者可能会想,为什么不检查错误?
然后添加错误检查。
最高级别的 doc 评论是包评论. 每个包应只包含一个包评论,提供高级别的概述包是什么以及如何使用它,包括代码和/或命令示例。 包评论可以在任何源文件中 - 而且只有这个文件 - 在包 <name>
声明之上出现。
与我们观察过的所有其他评论不同,包评论通常使用/*
和*/
,因为它们可以很长。
1/*
2Gofmt formats Go programs.
3It uses tabs for indentation and blanks for alignment.
4Alignment assumes that an editor is using a fixed-width font.
5
6Without an explicit path, it processes the standard input. Given a file,
7it operates on that file; given a directory, it operates on all .go files in
8that directory, recursively. (Files starting with a period are ignored.)
9By default, gofmt prints the reformatted sources to standard output.
10
11Usage:
12
13gofmt [flags] [path ...]
14
15The flags are:
16
17-d
18Do not print reformatted sources to standard output.
19If a file's formatting is different than gofmt's, print diffs
20to standard output.
21. . .
22*/
23package main
那么,关于doc评论的格式呢?他们可以(或必须)拥有什么样的结构?
Doc 评论有格式
根據 Go 創造者的 an old blog post :
Godoc在概念上与Python的Docstring和Java的Javadoc有关,但它的设计更简单。godoc所阅读的评论不是语言构造(与Docstring一样),也不一定有自己的机器可读语法(与Javadoc一样)。
虽然 doc 评论没有必要的格式,但它们可以选择使用在 Go 文档中完全描述的简化 Markdown 子集
的格式。 在 doc 评论中,您将写入段落和列表,显示示例代码或命令,提供引用链接等。
以下是从 How To Write Your First Program in Go的扩展Hello World程序中添加的一些评论:
1[label greeting.go]
2// This is a doc comment for greeting.go.
3// - prompt user for name.
4// - wait for name
5// - print name.
6// This is the second paragraph of this doc comment.
7// `gofmt` (and `go doc`) will insert a blank line before it.
8package main
9
10import (
11 "fmt"
12 "strings"
13)
14
15func main() {
16 // This is not a doc comment. Gofmt will NOT format it.
17 // - prompt user for name
18 // - wait for name
19 // - print name
20 // This is not a "second paragraph" because this is not a doc comment.
21 // It's just more lines to this non-doc comment.
22 fmt.Println("Please enter your name.")
23 var name string
24 fmt.Scanln(&name)
25 name = strings.TrimSpace(name)
26 fmt.Printf("Hi, %s! I'm Go!", name)
27}
上面的package main
评论是一个doc评论,它试图使用doc评论格式的段落和列表的概念,但它并不完全正确。
1// This is a doc comment for greeting.go.
2// - prompt user for name.
3// - wait for name.
4// - print name.
5//
6// This is the second paragraph of this doc comment.
7// `gofmt` (and `go doc`) will insert a blank line before it.
8package main
9
10import (
11 "fmt"
12 "strings"
13)
14
15func main() {
16 // This is not a doc comment. `gofmt` will NOT format it.
17 // - prompt user for name
18 // - wait for name
19 // - print name
20 // This is not a "second paragraph" because this is not a doc comment.
21 // It's just more lines to this non-doc comment.
22 fmt.Println("Please enter your name.")
23 var name string
24 fmt.Scanln(&name)
25 name = strings.TrimSpace(name)
26 fmt.Printf("Hi, %s! I'm Go!", name)
27}
注意这一点:
..................................................................................................................................................
运行go doc greeting.go
将格式化和打印 doc 评论,但不是在main()
中的评论:
1[label Output]
2This is a doc comment for greeting.go.
3 - prompt user for name.
4 - wait for name.
5 - print name.
6
7This is the second paragraph of this doc comment. `gofmt` (and `go doc`) will
8insert a blank line before it.
如果您使用此文档评论格式一致和适当,您的包的用户将感谢您的易于阅读的文档。
阅读 官方参考页面在doc评论,了解如何写得好的一切。
快速删除代码
你有没有写过一些新的代码来减慢你的应用程序,或者更糟糕的是,破坏了一切?这是另一次使用C风格的/*
和*/
标签。你可以快速禁用问题代码,将一个/*
放在它之前,一个*/
在它之后。
1[label problematic.go]
2. . .
3func main() {
4 x := initializeStuff()
5 /* This code is causing problems, so we're going to comment it out for now
6 someProblematicCode(x)
7 */
8 fmt.Println("This code is still running!")
9}
对于更长的代码片段,使用这些标签比在每个问题代码行开始时添加//
要方便得多。 作为一种惯例,使用//
用于普通的评论和文档评论,这些评论将无限期地生活在您的代码中。 在测试过程中仅暂时使用/*
和*/
标签(或先前提到的包评论)。
结论
通过在所有 Go 计划中写出表达式的评论,您是:
防止你的合作者破坏事物 2.帮助你的未来自我,有时忘记了为什么代码最初是写的 3.给你的包的用户一个参考,他们可以读,而不会潜入你的代码