聊聊Go语言的注释

​ 在我们着手编写Go代码的时候,是否有过考虑,该编写什么样的代码注释才会使得代码读起来易懂呢?不会出现我们经常开玩笑说的:”过了几个月,自己写的代码都不认识了”的情况呢?

​ 接下来让我们一起来聊聊Go语言的注释,包括了包注释、命令注释、类型注释、函数注释、变量/常量注释部分,希望能够帮助大家养成良好注释的习惯!

一、注释的格式

在Go语言中支持以下两种注释格式:

1.1 块注释

/**/是C风格的注释,常用于包的说明文档。同时在表达式中或者禁用大量的代码块非常有用。

/*
Package fmt implements formatted I/O with functions analogous
to C's printf and scanf.  The format 'verbs' are derived from C's but
are simpler.
.....
*/
package fmt

注:在Go语言包中的doc.go通常是包的文档性说明。

1.2 行注释

//是C++风格注释,在Go语言中比较常用。

// A fmt is the raw formatter used by Printf etc.
// It prints into a buffer that must be set up separately.
type fmt struct {
    buf *buffer
    ...
}

二、包注释

​ 每个程序包(Package)都应该有一个包注释,该注释介绍了整个Package相关的信息,并且通常设定了对Package的期望效果。

​ 包注释不仅仅可以使用块注释的格式,当然也可以使用行注释的格式,这两种格式在Go语言中都非常常用。

// Package path implements utility routines for manipulating slash-separated
// paths.
//
// The path package should only be used for paths separated by forward
// slashes, such as the paths in URLs. This package does not deal with
// Windows paths with drive letters or backslashes; to manipulate
// operating system paths, use the [path/filepath] package.
package path

让我们来解释一下示例中的包注释:

  • 包注释的第一句话
  1. // Package path:这是一个包注释,用于描述接下来的代码文件是一个名为path的包。这是Go语言约定的一部分,有助于在整个代码库中提供一致的文档。也就是说开头必须声明这个Package,Package后面接着是包的名称。
  2. implements utility routines for manipulating slash-separated paths.:这是对包功能的简要描述。它说明了该包的目的,即实现用于处理斜杠分隔路径的实用程序例程。
  • 包注释的其它语句
  1. 主要是针对包的功能具体使用方法进行一个说明。
  2. 当然也可以添加包的使用示例(可选)
/*
....
For example,

    fmt.Sprintf("%[2]d %[1]d\n", 11, 22)

will yield "22 11", while

    fmt.Sprintf("%[3]*.[2]*[1]f", 12.0, 2, 6)

equivalent to

    fmt.Sprintf("%6.2f", 12.0)
....
*/
package fmt

三、命令注释

​ 命令(Command)注释与包注释,但它描述的是程序的行为,而不是程序包中的功能特征。注释的第一句话的开头通常是Command的名称,需要首字母大写(因为是一行的开头)

/*
Gofmt formats Go programs.
It uses tabs for indentation and blanks for alignment.
Alignment assumes that an editor is using a fixed-width font.
...
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.
    -w
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, overwrite it
        with gofmt's version. If an error occurred during overwriting,
        the original file is restored from an automatic backup.
...
*/
package main

​ 注:命令注释通常使用块注释来表示,内容主要包括:命令的功能、命令的用法及参数说明等

四、类型注释

4.1 实例注释

​ 类型(type)注释应该解释type中的每个实例代表了什么。注释的第一句话的开头通常是type的名称或者添加一个A,需要首字母大写(因为是一行的开头)

package zip

// A Reader serves content from a ZIP archive.
type Reader struct {
    ...
}

4.2 并发安全注释

默认情况下,一个类型被单个goroutine使用是安全的,如果一个类型支持并发使用的话,那么文档注释应该说明它。

package regexp

// Regexp is the representation of a compiled regular expression.
// A Regexp is safe for concurrent use by multiple goroutines,
// except for configuration methods, such as Longest.
type Regexp struct {
    ...
}

4.3 零值注释

如果一个类型的零值是有意义的,那么也应当进行说明

package bytes

// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
    ...
}

4.4 导出字段注释

对于类型中的每一个具有导出意义的字段(也就是开头首字母是大写)都应该进行说明

package io

// A LimitedReader reads from R but limits the amount of
// data returned to just N bytes. Each call to Read
// updates N to reflect the new amount remaining.
// Read returns EOF when N <= 0.
type LimitedReader struct {
    R   Reader // underlying reader
    N   int64  // max bytes remaining
}

五、函数注释

5.1 参数/返回值/函数作用注释

​ 函数(func)注释应该解释函数的参数、返回值含义,同时应该解释函数的作用。

package strconv
// 例子1:解释了返回值的含义和函数功能
// Quote returns a double-quoted Go string literal representing s.
// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100)
// for control characters and non-printable characters as defined by IsPrint.
func Quote(s string) string {
    ...
}
// 例子2:解释了参数的含义和函数功能
package os
// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
// The program terminates immediately; deferred functions are not run.
//
// For portability, the status code should be in the range [0, 125].
func Exit(code int) {
    ...
}

5.2 bool返回值函数注释

对于bool返回值的函数,通常使用reports whether来描述函数功能,而不是使用or not

package strings

// HasPrefix reports whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool

5.3 形参/返回值名称注释

函数的形参的命名和返回值的命名必须要见名知意,并且可以将它们添加到文档注释中,使得文档注释更容易理解。

package io

// Copy copies from src to dst until either EOF is reached
// on src or an error occurs. It returns the total number of bytes
// written and the first error encountered while copying, if any.
//
// A successful Copy returns err == nil, not err == EOF.
// Because Copy is defined to read from src until EOF, it does
// not treat an EOF from Read as an error to be reported.
func Copy(dst Writer, src Reader) (n int64, err error) {
    ...
}

5.4 并发安全注释

同样的,和类型注释一样,若函数是并发安全的,那么也需要在函数注释中说明。

package sql

// Close returns the connection to the connection pool.
// All operations after a Close will return with ErrConnDone.
// Close is safe to call concurrently with other operations and will
// block until all other operations finish. It may be useful to first
// cancel any used context and then call Close directly after.
func (c *Conn) Close() error {
    ...
}

5.5 特殊例子注释

当然,如果函数特殊例子,那么函数注释也应当说明:

package math

// Sqrt returns the square root of x.
//
// Special cases are:
//
//  Sqrt(+Inf) = +Inf
//  Sqrt(±0) = ±0
//  Sqrt(x < 0) = NaN
//  Sqrt(NaN) = NaN
func Sqrt(x float64) float64 {
    ...
}

5.6 算法功能注释

函数注释不应解释内部细节,例如当前实现中使用的算法。这些最好留给函数体内部的注释。这样做的好处是: 将来可以更加容易的将该函数更改为不同的算法,而不需要改动文档。

package sort

// Sort sorts data in ascending order as determined by the Less method.
// It makes one call to data.Len to determine n and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
    ...
}

六、常量/变量注释

6.1 分组注释

​ 可以对常量(const)/变量(variable)进行分组表示,同时一般使用单行注释来说明。

package scanner // import "text/scanner"

// The result of Scan is one of these tokens or a Unicode character.
const (
    EOF = -(iota + 1)
    Ident
    Int
    Float
    Char
    ...
)

6.2 组内元素注释

有时候,常量/变量里面的每一个元素都需要记录其作用

package unicode // import "unicode"

const (
    MaxRune         = '\U0010FFFF' // maximum valid Unicode code point.
    ReplacementChar = '\uFFFD'     // represents invalid code points.
    MaxASCII        = '\u007F'     // maximum ASCII value.
    MaxLatin1       = '\u00FF'     // maximum Latin-1 value.
)

6.3 未分组元素注释

另一方面,未分组的常量/变量的注释开头通常为名称。例如:

package unicode

// Version is the Unicode edition from which the tables are derived.
const Version = "13.0.0

6.4 类型化元素注释

类型化常量/变量显示在其类型的声明旁边,因此通常会省略常量/变量注释,而使用该类型的文档注释。

package syntax

// An Op is a single regular expression operator.
type Op uint8

const (
    OpNoMatch        Op = 1 + iota // matches no strings
    OpEmptyMatch                   // matches empty string
    OpLiteral                      // matches Runes sequence
    OpCharClass                    // matches Runes interpreted as range pair list
    OpAnyCharNotNL                 // matches any character except newline
    ...
)

好啦,本文到此就结束喽!介绍了五种类型的注释方法,希望能对您编写良好的Go语言注释有所帮助。若文章中出现任何纰漏,欢迎大家指正批评哦!如果觉得写得还不错的话,麻烦大家点赞收藏加关注哦!

参考文章:
The Go Programming Language Specification