1 - 注释
Go语言实战
摘录自 Go语言实战 3.5.3 Go 语言的文档一节
为了在 godoc 生成的文档里包含自己的代码文档,开发人员需要用下面的规则来写代码和注释。
在标识符之前,把文档作为注释加入到代码中,这个方式对包、函数、类型和全局变量都适用。注释可以以双斜线开头,也可以用斜线和星号风格:
// Retrieve 连接到配置库,收集各种链接设置、用户名和密码。这个函数在成功时
// 返回 config 结构,否则返回一个错误。
func Retrieve() (config, error) {
// ...省略
}
如果想给包写一段文字量比较大的文档,可以在工程里包含一个叫作 doc.go 的文件,使用同样的包名,并把包的介绍使用注释加在包名声明之前:
/*
包 usb 提供了用于调用 USB 设备的类型和函数。想要与 USB 设备创建一个新链接,使用 NewConnection
...
*/
package usb
这段关于包的文档会显示在所有类型和函数文档之前。这个例子也展示了如何使用斜线和星 号做注释。
Golang语言规范
https://golang.org/ref/spec#Comments
注释作为程序文档。有两种形式:
- 行注释以字符序列
//
开始,止于行末。 - 一般注释以字符序列
/*
开始,并以随后的第一个字符序列*/
停止。
注释不能在符文或字符串字面值内部开始,也不能在注释内部开始。一个不包含换行符的一般注释就像一个空格。任何其他注释的作用就像一个换行符。
Effective Go
https://golang.org/doc/effective_go.html#commentary
Go 提供了 C风格 /* */
块注释和 C++风格的 //
行注释。行注释是标准的;块注释主要是作为包注释出现的,但在表达式中或禁用大片代码时很有用。
godoc(程序和 web 服务器) 处理 Go 源文件,以提取有关包内容的文档。出现在顶层声明之前的注释,没有中间的换行符,与声明一起被提取出来,作为该项目的解释文本。这些注释的性质和风格决定了 godoc 产生的文档的质量。
每个包都应该有一个包注释,即在包子句之前的块注释。对于多文件的包,包注释只需要出现在一个文件中,任何一个文件都可以。包的注释应该介绍包,并提供与包整体相关的信息。它将首先出现在 godoc 页面上,并且应该构建后面的详细文档。
/*
Package regexp implements a simple library for regular expressions.
The syntax of the regular expressions accepted is:
regexp:
concatenation { '|' concatenation }
concatenation:
{ closure }
closure:
term [ '*' | '+' | '?' ]
term:
'^'
'$'
'.'
character
'[' [ '^' ] character-ranges ']'
'(' regexp ')'
*/
package regexp
如果包的内容简单,包的注释可以简短一些。
// Package path implements utility routines for
// manipulating slash-separated filename paths.
注释不需要额外的格式化,比如星星的横幅。生成的输出可能甚至不会以固定宽度的字体呈现,所以不要依赖对齐的间距–godoc,像gofmt一样,会照顾到这一点。注释是未解释的纯文本,所以 HTML 和其他注释(如 _this_
)会逐字重现,不应使用。godoc 做的一个调整是用固定宽度的字体显示缩进的文本,适合程序片段。fmt包的注释就很好地利用了这一点。
根据上下文的不同,godoc甚至可能不会重新格式化注释,所以确保它们直接看起来不错:使用正确的拼写、标点和句子结构,折叠长行,等等。
在一个包内,任何紧接在顶层声明之前的注释都会作为该声明的 doc 注释。程序中的每一个导出(大写)的名字都应该有一个 doc 注释。
doc注释最好是作为完整的句子来使用,这样就可以有各种各样的自动呈现方式。第一句话应该是一句话的总结,以被声明的名称开始。
// Compile parses a regular expression and returns, if successful,
// a Regexp that can be used to match against text.
func Compile(str string) (*Regexp, error) {
如果每个doc注释都以它所描述的物品名称开头,你可以使用go工具的doc子命令,通过grep运行输出。想象一下,你记不住 “Compile” 这个名字,但又在寻找正则表达式的解析函数,所以你运行了这个命令:
$ go doc -all regexp | grep -i parse
如果包中所有的 doc 注释都以 “This function…” 开头,grep 就不会帮你记住这个名字。但因为软件包中的每条文档注释都以名称开头,你会看到这样的内容,它能让你想起你要找的单词。
$ go doc -all regexp | grep -i parse
Compile parses a regular expression and returns, if successful, a Regexp
MustCompile is like Compile but panics if the expression cannot be parsed.
parsed. It simplifies safe initialization of global variables holding
$
Go的声明语法允许对声明进行分组。一个文档注释可以介绍一组相关的常量或变量。由于整个声明都会被介绍,所以这样的注释往往可以敷衍了事。
// Error codes returned by failures to parse an expression.
var (
ErrInternal = errors.New("regexp: internal error")
ErrUnmatchedLpar = errors.New("regexp: unmatched '('")
ErrUnmatchedRpar = errors.New("regexp: unmatched ')'")
...
)
分组也可以表示项目之间的关系,比如一组变量是由一个mutex保护的。
var (
countLock sync.Mutex
inputCount uint32
outputCount uint32
errorCount uint32
)
2 - Token
备注:摘录自 golang语言规范 https://golang.org/ref/spec#Tokens
Token
Token构成了go语言的词汇。有四类Token:identifiers(标识符)、keywords(关键字)、operators and punctuation(运算符和标点符号)、以及literals(字面量)。
由空格(U+0020)、水平制表符(U+0009)、回车符(U+000D)和换行符(U+000A)形成的空白(whitespace)会被忽略,除非它是用来分隔Token,避免多个Token合并成一个Token。
此外,换行或文件末尾可能会触发分号的插入。
在将输入内容分解成Token的同时,下一个Token是形成有效Token的最长字符序列。
分号
在一些产品中正式语法使用分号"; “作为终结符。Go程序可以使用以下两条规则来省略大部分的分号:
-
当输入的内容被分解成Token时,分号会自动插入到Token流中,紧接在一行的最后一个Token之后,如果该Token是… …
- identifier/标识符
- 整型,浮点,虚数,rune(符文)或者 字符串字面值
- break、continue、fallthrough、return等关键字之一
- 运算符和标点符号
++, --, ), ], 或 }
中的一种
-
为了让复杂的语句只占一行,可以在结尾”) “或”}“前省略分号。
3 - 标识符
Identifiers/标识符
https://golang.org/ref/spec#Identifiers
identifiers/标识符用于命名程序实体,如变量和类型。标识符是由一个或多个字母和数字组成的序列。标识符的第一个字符必须是字母(不能是数字开头)。
identifier = letter { letter | unicode_digit } .
a
_x9
ThisVariableIsExported
αβ
空白标识符
https://golang.org/ref/spec#Blank_identifier
空白标识符由下划线字符 _
表示。它作为匿名的占位符,而不是常规的(非空白)标识符,在声明、操作数和赋值中具有特殊意义。
预定义的标识符
universe block(宇宙块)中隐式声明了以下标识符:
Types:
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
Constants:
true false iota
Zero value:
nil
Functions:
append cap close complex copy delete imag len
make new panic print println real recover
导出的标识符
标识符可以导出,以便从另一个包中访问它。标识符在以下两种情况下被导出:
- 标识符名称的第一个字符是Unicode大写字母(Unicode class “Lu”);和
- 标识符是在package block(包块)中声明的,或者是字段名或方法名。
所有其他的标识符都不会被导出。
标识符的唯一性
给定一组标识符,如果一个标识符与集合中的其他标识符不同,则称为唯一标识符。如果两个标识符的拼写不同,或者它们出现在不同的包中并且没有被导出,那么它们就是不同的。否则,它们是相同的。
4 - 关键字
Keywords/关键字
https://golang.org/ref/spec#Keywords
以下是保留的关键词,不得作为标识符使用。
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
5 - 运算符
Operators and punctuation/运算符和标点符号
https://golang.org/ref/spec#Operators_and_punctuation
以下字符序列代表运算符(包括赋值运算符)和标点符号。
+ & += &= && == != ( )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= ++ = := , ;
% >> %= >>= -- ! ... . :
&^ &^=
6 - 字面量
备注:摘录自 golang语言规范 https://golang.org/ref/spec#Integer_literals
Integer literals/整型字面量
整数字面量是代表整数常数的数字序列。可选的前缀设置了一个非十进制的基数:0b或
0B代表二进制,0,
0o, 或
0O代表八进制,0x或
0X代表十六进制。单一的0被认为是十进制的0。在十六进制中,字母a到f和A到F代表10到15的数值。
为了便于阅读,下划线字符_可能会出现在基数前缀之后或连续的数字之间;这种下划线不会改变文字的价值。
int_lit = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
binary_lit = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit = "0" ( "x" | "X" ) [ "_" ] hex_digits .
decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits = binary_digit { [ "_" ] binary_digit } .
octal_digits = octal_digit { [ "_" ] octal_digit } .
hex_digits = hex_digit { [ "_" ] hex_digit } .
42
4_2
0600
0_600
0o600
0O600 // 第二个字是大写字母 'O'
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727
_42 // 标识符,不是整型字面量
42_ // invalid: _ 必须隔开连续的数字
4__2 // invalid: 一次只能有一个_
0_xBadFace // invalid: _ 必须隔开连续的数字
Floating-point literals/浮点字面量
浮点字面量是浮点常数的十进制或十六进制表示。
十进制浮点字面量由整数部分(小数点)、小数点、分数部分(小数点)和指数部分(e或E后面跟着一个可选的符号和小数点)组成。整数部分或小数部分中的其中一个可以省略;小数点或指数部分中的其中一个可以省略。指数值exp将mantissa(整数和小数部分)的比例为10exp。
十六进制浮点文字由0x或0X前缀、整数部分(十六进制数字)、小数点、小数部分(十六进制数字)和指数部分(p或P,后面跟着一个可选的符号和十进制数字)组成。整数部分或分数部分中的其中一个可以省略;弧度点也可以省略,但指数部分是必须的。(这个语法与IEEE 754-2008 §5.12.3中给出的语法相匹配。)指数值exp将尾数(整数和分数部分)按2exp缩放。
为了便于阅读,下划线字符_可以出现在基数前缀之后或连续的数字之间;这种下划线不会改变字面值。
float_lit = decimal_float_lit | hex_float_lit .
decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
decimal_digits decimal_exponent |
"." decimal_digits [ decimal_exponent ] .
decimal_exponent = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .
hex_float_lit = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
hex_mantissa = [ "_" ] hex_digits "." [ hex_digits ] |
[ "_" ] hex_digits |
"." hex_digits .
hex_exponent = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .
0.
72.40
072.40 // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5. // == 15.0
0.15e+0_2 // == 15.0
0x1p-2 // == 0.25
0x2.p10 // == 2048.0
0x1.Fp+0 // == 1.9375
0X.8p-0 // == 0.5
0X_1FFFP-16 // == 0.1249847412109375
0x15e-2 // == 0x15e - 2 (integer subtraction)
0x.p1 // invalid: mantissa has no digits
1p-2 // invalid: p exponent requires hexadecimal mantissa
0x1.5e-2 // invalid: hexadecimal mantissa requires p exponent
1_.5 // invalid: _ must separate successive digits
1._5 // invalid: _ must separate successive digits
1.5_e1 // invalid: _ must separate successive digits
1.5e_1 // invalid: _ must separate successive digits
1.5e1_ // invalid: _ must separate successive digits
Imaginary literals/虚数字面量
虚数字面量表示复数常数的虚数部分,它由一个整数或浮点字组成,后面是小写字母i。
imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .
为了向后兼容,虚字的整数部分完全由十进制数字(可能还有下划线)组成,即使它以前导0开始,也被认为是一个十进制整数。
0i
0123i // == 123i for backward-compatibility
0o123i // == 0o123 * 1i == 83i
0xabci // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i // == 0x1p-2 * 1i == 0.25i
Rune literals / 符文字面量
符文字面量代表一个符文常量,是一个用于识别Unicode code point的整数值。符文字面量表示为一个或多个用单引号括起来的字符,如’x’或’\n’。在引号内,除了换行和未转义的单引号外,任何字符都可以出现。单引号字符代表字符本身的Unicode值,而以反斜杠开头的多字符序列则以各种格式编码值。
最简单的形式代表引号内的单个字符;由于go源文本是以UTF-8编码的Unicode字符,所以多个UTF-8编码的字节可以代表单个整数值。例如,字面量 ‘a’ 持有一个字节,代表字面量 a,Unicode U+0061,值0x61,而 “ä “持有两个字节(0xc3 0xa4),代表文字 “a-dieresis”,U+00E4,值0xe4。
几个反斜杠转义符允许任意值被编码为ASCII文本。有四种方法可以将整数值表示为数字常数。\x
后面跟着两位十六进制数字;\u
后面跟着四位十六进制数字;\U
后面跟着八位十六进制数字,以及后面跟着三位八进制数字的反斜杠\
。在每一种情况下,字面量的值都是相应基数的数字所代表的值。
虽然这些表示方法都会产生一个整数,但它们的有效范围不同。八进制转义符必须在0到255之间表示一个值。十六进制转义符通过结构满足这个条件。转义符\u和\U代表Unicode码点,所以在它们里面有些值是非法的,特别是那些高于0x10FFFF的值和代用的一半。
在反斜杠之后,某些单字符转义符代表特殊值:
\a U+0007 alert or bell
\b U+0008 backspace
\f U+000C form feed
\n U+000A line feed or newline
\r U+000D carriage return
\t U+0009 horizontal tab
\v U+000b vertical tab
\\ U+005c backslash
\' U+0027 single quote (valid escape only within rune literals)
\" U+0022 double quote (valid escape only within string literals)
所有其他以反斜杠开头的序列在符文字里面都是非法的。
rune_lit = "'" ( unicode_value | byte_value ) "'" .
unicode_value = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value = octal_byte_value | hex_byte_value .
octal_byte_value = `\` octal_digit octal_digit octal_digit .
hex_byte_value = `\` "x" hex_digit hex_digit .
little_u_value = `\` "u" hex_digit hex_digit hex_digit hex_digit .
big_u_value = `\` "U" hex_digit hex_digit hex_digit hex_digit
hex_digit hex_digit hex_digit hex_digit .
escaped_char = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .
'a'
'ä'
'本'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
'\'' // rune literal containing single quote character
'aa' // illegal: too many characters
'\xa' // illegal: too few hexadecimal digits
'\0' // illegal: too few octal digits
'\uDFFF' // illegal: surrogate half
'\U00110000' // illegal: invalid Unicode code point
String literals/字符串字面量
字符串字面量表示从字符序列的连接中获得的字符串常量。有两种形式:原始(raw)字符串字元和解释(interpreted)字符串字元。
原始字符串字元是后引号之间的字符序列,如 “`foo`"。在引号内,除了后引号,任何字符都可以出现。原始字符串字面量的值是由引号之间未解释(隐含UTF-8编码)的字符组成的字符串;特别是,反斜杠没有特殊意义,字符串可能包含换行符。原始字符串字元中的回车字符(’\r’)会从原始字符串值中被丢弃。
被解释的字符串字面量是双引号之间的字符序列,如 “bar”。在引号内,除了换行和未转义的双引号外,任何字符都可以出现。引号之间的文字构成了字面量意义的值,反斜杠转义的解释与符文字面意义的解释相同(除了’/‘是非法的,而”/“是合法的),有相同的限制。三位数的八进制(\nnnnn)和两位数的十六进制(\xnn)转义符代表结果字符串的单个字节;所有其他转义符代表单个字符的UTF-8编码(可能是多字节)。因此,在一个字符串中,字面意义中的\377和\xFF代表一个价值0xFF=255的单一字节,而ÿ、\u00FF、\U000000FF和xc3\xbf代表字符U+00FF的UTF-8编码的两个字节0xc3 0xbf。
string_lit = raw_string_lit | interpreted_string_lit .
raw_string_lit = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
`abc` // same as "abc"
`\n
\n` // same as "\\n\n\\n"
"\n"
"\"" // same as `"`
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800" // illegal: surrogate half
"\U00110000" // illegal: invalid Unicode code point
这些例子都代表同一个字符串:
"日本語" // UTF-8 input text
`日本語` // UTF-8 input text as a raw literal
"\u65e5\u672c\u8a9e" // the explicit Unicode code points
"\U000065e5\U0000672c\U00008a9e" // the explicit Unicode code points
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" // the explicit UTF-8 bytes
如果源码将一个字符表示为两个code point,比如涉及重音和字母的组合形式,如果放在符文rune字面量中,结果将是一个错误(它不是单个的code point),如果放在字符串字面量中,将出现两个code point。