如何用 Go 写评论

介绍

几乎所有的编程语言都有添加评论的语法,而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声明的评论也是有用的,但以不同的方式。

博士评论

funcconstvartype等顶级(非标注)声明的评论被称为doc评论

<$>[注] :在Go中, _exported_在某些语言中意味着相同的事情:一个导出组件是其他包在导入您的包时可能使用的组件。

Doc 评论 解释 WhatHow

与我们刚刚看到的普通评论不同,doc评论通常解释了代码的作用或它如何做到这一点,因为它们不是针对包的维护者,而是针对用户,他们通常不希望阅读或贡献代码。

用户通常会在三个地方读到您的doc评论:

  1. 在本地终端中,在单独的源文件或目录上运行go doc
  2. pkg.go.dev,任何公共 Go 包的官方文件中心
  3. 在私人运营的 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.给你的包的用户一个参考,他们可以读,而不会潜入你的代码

Published At
Categories with 技术
Tagged with
comments powered by Disqus