找回密码
 立即注册
首页 业界区 安全 go~基本知识参考

go~基本知识参考

茹静曼 2025-6-1 21:27:34
介绍

这是一个 Go 语言的参考手册,你也可以访问golang.org获取更多信息和其他文档。
Go 是在设计时考虑了系统编程的通用型编程语言。它是强类型,有垃圾回收机制并原生支持并发编程。Go 程序由一个或多个 package 组成,这样可以高效的管理依赖。
Go 的语法简洁且有规则,这让自动化工具可以很容易的分析代码,例如:集成开发环境。
标记

语法采用扩展巴科斯范式。
  1. Production  = production_name "=" [ Expression ] "." .
  2. Expression  = Alternative { "|" Alternative } .
  3. Alternative = Term { Term } .
  4. Term        = production_name | token [ "…" token ] | Group | Option | Repetition .
  5. Group       = "(" Expression ")" .
  6. Option      = "[" Expression "]" .
  7. Repetition  = "{" Expression "}" .
复制代码
产生式是由词法单元和以下操作符构成的表达式(优先级依次递增):
  1. |   或
  2. ()  分组
  3. []  可选 (出现 0 或 1 次)
  4. {}  可重复 (出现 0 到 n 次)
复制代码
小写的产生式名称用来与词法单元区分。非终结符采用驼峰式。词法单元由双引号或反引号组成。
a...b 表示从 a 到 b 之间的任意字符。省略号 ... 也可以在规范中表示对更详细的枚举和代码片段的省略。字符 ... 不是 Go 语言的词法单元。
源码表示法

Go 的源代码使用 UTF-8 编码的 Unicode 文本。不过它并不是完全规范化的,单重音的代码点与由相同字符和音标组成的代码点是不同的;前者我们认为它是两个代码点。简单来讲,文档会在源代码文本中使用非规范的术语字符来表示一个 Unicode 代码点。
每个代码点都是不同的;相同字符的大写和小写形式表示不同的字符。
实现限制:为了兼容其他工具,编译器不允许出现 Utf-8 编码的源文本中的 NUL 字符(U+0000)。
实现限制:为了兼容其他工具,如果源文本中是以Utf-8 编码的字节序标记(U+FEFF)为起始代码点。编译器会忽略它。字节序标记不应出现在源文本的任何位置。
字符

这些单词表示 Unicode 字符的类别:
  1. newline        = /* Unicode 代码点 U+000A */ .
  2. unicode_char   = /* 排除换行以外的任意 Unicode 代码点 */ .
  3. unicode_letter = /* 一个字母("Letter")类型的 Unicode 代码点  */ .
  4. unicode_digit  = /* 一个数字("Number, decimal digit")类型的 Unicode 代码点  */ .
复制代码
在 Unicode8.0 标准中,第 4.5 章节 “一般类别” 中定义了字符的类别。Go 能够处理任何字符集,包括 Lu,Li,Lt,Lm 或 Lo 作为 Unicode 字母,还可以把数字字符集 Nd 当作 Unicode 数字处理。
字母和数字

我们认为下划线 _ (U+005F)是一个字母:
  1. letter        = unicode_letter | "_" .
  2. decimal_digit = "0" … "9" .
  3. octal_digit   = "0" … "7" .
  4. hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" .
复制代码
词汇元素

注释

注释是程序的说明文档。在 Go 中有两种形式:

  • 单行注释从 // 开始直到行末结束。
  • 通用注释从 /* 开始直到 */ 结束。
注释不能嵌套在其他注释、字符串和 rune 的字面值中。不包含换行符的通用注释之间通过空格符连接,其他情况下每段注释都会另起一行。
词汇元素

词汇元素构成了 Go 语言的词汇表。它有四种类型:标识符、关键字、操作符/标点符号、字面值。空白符可以是空格(U+0020)、水平制表符(U+0009)、换行符(U+000D)或换行符(U+000A)。它本身会被忽略,一般用来区分不同的词汇元素。换行符或文件终止符(EOF)还可能触发编译程序在源代码的行末或文件末尾追加分号。在分解源代码的词汇元素的过程中,会把当前可以形成有效词汇元素的最长字符序列作为下一个词汇元素。
分号

正规语法在很多产生式中使用分号 ";" 作为终结符。Go 程序中遵循下面两条规则省略了大部分的分号:

  • 当某行的最后一个词汇元素是以下元素时自动补全分号:


  • 一个标识符。
  • 一个整数,浮点数,虚数,rune 或字符串字面值。
  • 关键字 break、continue、fallthrough 和 return 其中之一。
  • 操作符/标点符号 ++,--,),] 和 } 其中之一。

  • 为了支持独占一行的复杂语句,会省略与 ")" 或 "}" 相邻的分号。
为了反应惯用用途,本篇文档的所有例子都基于以上规则省略分号。
标识符

标识符表示程序实体单元,例如:变量、类型。一个标识符由一个或多个字母和数字组成。标识符的首字符必须为字母。
  1. identifier = letter { letter | unicode_digit } .
复制代码
  1. a
  2. _x9
  3. ThisVariableIsExported
  4. αβ
复制代码
Go 已经预定义了一些标识符。
关键字

以下关键字是预留的,它们不能作为标识符:
  1. break        default      func         interface    select
  2. case         defer        go           map          struct
  3. chan         else         goto         package      switch
  4. const        fallthrough  if           range        type
  5. continue     for          import       return       var
复制代码
操作符和标点符号

以下字符序列用于表示操作符(包括赋值运算符)和标点符号:
  1. +    &     +=    &=     &&    ==    !=    (    )
  2. -    |     -=    |=     ||    <     <=    [    ]
  3. *    ^     *=    ^=     <-    >     >=    {    }
  4. /    <<    /=    <<=    ++    =     :=    ,    ;
  5. %    >>    %=    >>=    --    !     ...   .    :
  6.      &^          &^=
复制代码
整型字面值

整型字面值是一个数字序列,相当于整型常量。可以使用前缀指定非小数进制:0 表示八进制,0x/0X 表示十六进制。在十六进制字面值中,字母 a-f 和 A-F 都表示数字 10-15。
  1. int_lit     = decimal_lit | octal_lit | hex_lit .
  2. decimal_lit = ( "1" … "9" ) { decimal_digit } .
  3. octal_lit   = "0" { octal_digit } .
  4. hex_lit     = "0" ( "x" | "X" ) hex_digit { hex_digit } .
复制代码
  1. 42
  2. 0600
  3. 0xBadFace
  4. 170141183460469231731687303715884105727
复制代码
浮点字面值

浮点字面值是一个小数,相当于浮点数常量。它由整数部分,小数点,小数部分和指数部分构成。整数部分和小数部分用小数点链接;指数部分由  e / E 字符后接一个有符号指数构成。整数部分和小数部分可以省略其一;小数点和指数部分可以省略其一。
  1. float_lit = decimals "." [ decimals ] [ exponent ] |
  2.             decimals exponent |
  3.             "." decimals [ exponent ] .
  4. decimals  = decimal_digit { decimal_digit } .
  5. exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals .
复制代码
  1. 0.
  2. 72.40
  3. 072.40  // == 72.40
  4. 2.71828
  5. 1.e+0
  6. 6.67428e-11
  7. 1E6
  8. .25
  9. .12345E+5
复制代码
虚数字面值

虚数字面值是一个小数,相当于复数常量中的虚数部分。它由浮点数或者整数后接小写字母 i 构成。
  1. imaginary_lit = (decimals | float_lit) "i" .
复制代码
  1. 0i
  2. 011i  // == 11i
  3. 0.i
  4. 2.71828i
  5. 1.e+0i
  6. 6.67428e-11i
  7. 1E6i
  8. .25i
  9. .12345E+5i
复制代码
Rune 字面值

rune 类型字面值相当于一个 rune 常量。它是一个表示 Unicode 代码点的整数。rune 类型字面值表示为用单引号包裹的一个或多个字符,像 'x' 或 '\n'。在单引号中除了换行符和未转义的单引号其他的字符都可以直接显示。单引号包裹的字符的值和字符在 Unicode 编码中的值相等,而以反斜线开头的多字符序列会把值翻译成多种格式。
使用引号表示单字符是最简单的方式;因为 Go 的源文本是 UTF-8 编码,一个整数可能代表多个 UTF-8 字节。例如, 'a' 可以使用单字节表示字符 a,Unicode 编码 U+0061,值 0x61,而 'ä' 是两字节表示分音符的 a,Unicode 编码 U+00E4,值 0xe4。
反斜线能将任意值编码成 ASCII 文本。有四种方式将整数值表示为数字常量:\x 后接两个十六进制数;\u 后接四个十六进制数;\U 后接八个十六进制数。 \ 后接三个八进制数。每种情况下都使用相应进制来表示字面量的整数值。
虽然这四种方式都以整数表示,但它们的有效区间并不相同。八进制只能表示 0 - 255 以内的整数。十六进制满可以满足需求。\u 和 \U  都可以表示 Unicode 代码点,不过其中的一些值是无效的,特别是 0x10FFFF 以上的值。
反斜线结合以下字符具有特殊含义:
  1. \a   U+0007 alert or bell
  2. \b   U+0008 退格符
  3. \f   U+000C form feed
  4. \n   U+000A line feed or newline
  5. \r   U+000D carriage return
  6. \t   U+0009 水平制表符
  7. \v   U+000b 垂直制表符
  8. \\   U+005c 反斜线
  9. \'   U+0027 单引号  (只在 rune 字面值中有效)
  10. "   U+0022 双引号  (只在字符串字面值中有效)
复制代码
其他所有以反斜线开头的序列在 rune 的规则中都是非法的。
  1. rune_lit         = "'" ( unicode_value | byte_value ) "'" .
  2. unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
  3. byte_value       = octal_byte_value | hex_byte_value .
  4. octal_byte_value = `\` octal_digit octal_digit octal_digit .
  5. hex_byte_value   = `\` "x" hex_digit hex_digit .
  6. little_u_value   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
  7. big_u_value      = `\` "U" hex_digit hex_digit hex_digit hex_digit
  8.                            hex_digit hex_digit hex_digit hex_digit .
  9. escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .
复制代码
  1. 'a'
  2. 'ä'
  3. '本'
  4. '\t'
  5. '\000'
  6. '\007'
  7. '\377'
  8. '\x07'
  9. '\xff'
  10. '\u12e4'
  11. '\U00101234'
  12. '\''         // 包含单引号的 rune 字面值
  13. 'aa'         // 无效: 太多字符
  14. '\xa'        // 无效: 缺少十六进制数
  15. '\0'         // 无效: 缺少八进制数
  16. '\uDFFF'     // 无效: surrogate half
  17. '\U00110000' // 无效: 非法的 Unicode 代码点
复制代码
字符串字面量

字符串字面量表示从字符序列中获取的字符串常量。它有两种格式:原始字符串字面量和解释型字符串字面量。
原始字符串是由反引号包裹(foo)。字符串中除反引号以外的其他字符都会显示出来。原生字符串由反引号之间的(默认 UTF-8 编码)的字符组成。它的值为引号内未经解释(默认 UTF-8 编码)所有字符;尤其是,反斜线再字符串中没有特殊意义并且字符串中保留换行符。在原始字符串的值中会丢弃回车键返回 '\r' 字符。
解释型字符串由双引号之间的字符组成("bar")。除了换行符和双引号其他字符都会显示出来。双引号之间的文本组成字面量的值。反斜线的转义规则与 rune 字面量基本相同(不同的是 \’ 非法,而 " 合法)。三位八进制数(\nnn)和两位十六进制数(\xnn)换码符的值表示相应字符串的字节。其他的换码符都表示字符各自的 UTF-8 编码(可能是多字节)。因此字符串 \377 和 \xFF 都表示值为 0xFF=255 的单个字节,而  ÿ, \u00FF, \U000000FF 和 \xc3\xbf 表示 UTF-8 编码字符 U+00FF 的两个字节 0xc3 0xbf。
  1. string_lit             = raw_string_lit | interpreted_string_lit .
  2. raw_string_lit         = "`" { unicode_char | newline } "`" .
  3. interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
复制代码
  1. `abc`                // 等价于 "abc"
  2. `\n
  3. \n`                  // 等价于 "\\n\n\\n"
  4. "\n"
  5. """                 // 等价于 `"`
  6. "Hello, world!\n"
  7. "日本語"
  8. "\u65e5本\U00008a9e"
  9. "\xff\u00FF"
  10. "\uD800"             // 无效: surrogate half
  11. "\U00110000"         // 无效: 无效的 Unicode 代码点
复制代码
这些例子都表示相同的字符串:
  1. "日本語"                                 // UTF-8 文本
  2. `日本語`                                 // UTF-8 文本作为原生字面值
  3. "\u65e5\u672c\u8a9e"                    // 确定的 Unicode 代码点
  4. "\U000065e5\U0000672c\U00008a9e"        // 确定的 Unicode 代码点
  5. "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"  // 确定的 UTF-8 字节
复制代码
如果源代码中使用两个代码点表示一个字符,例如带音标的字母,把它放在 rune 中会报错(它不是单代码点)。并且在字符串中会显示两个代码点。
常量

常量分为:布尔型,rune型,整型,浮点型,复数型,字符串型。其中 rune,整型,浮点型,复数型统称为数字常量。
常量的值可以表示为一个 rune字面量,整数字面量,浮点数字面量,虚数字面量,字符串字面量,表示常量的标识符,常量表达式,一个转换结果为常量的类型转换,和一些返回值为常量的内置函数(接受任何值的unsafe.Sizeof,接受部分表达式的cap 或 len,接受虚数常量的real 和 imag,接受数字常量的 complex)。布尔类型的值为预定义常量 true 或 false,预定义的标识符 iota 表示一个整型常量。
一般情况下复数常量是常量表达式的一种形式。会在常量表达式章节详细讨论。
数字常量可以表示任意精度的确定值而且不会溢出。因此,没有常量可以表示非 0,无穷大和非数字值。
常量可以指定类型也可以不指定类型。字面值常量,true,false,iota,和只包含无类型常量操作的常量表达式是无类型的。
常量可以通过常量声明和转换时显式的指定具体类型,也可以隐式的在变量声明、赋值或作为表达式操作元时隐式的指定具体类型。如果常量的值和他的类型不匹配,会报错。
无类型常量由一个默认的类型,这个类型会根据使用常量时的上下文进行隐式转换。例如:短变量声明 i := 0 没有指定 i 的类型。无类型常量的默认类型可以是:bool,rune,int,float64,complex128 或者 string,具体选择哪种类型由常量的值决定。
实现限制:虽然数字常量在 Go 中是任意精度,不过编译器在实现时会在内部限制精度。这意味着每个编译器实现都要:

  • 至少保证整形常量有 256 位
  • 浮点数常量(包括复数常量)都要保证至少 256 位的主体部分和至少 16 位的有符号指数部分
  • 如果不能表示给定整数的精度抛出错误
  • 如果浮点数或复数溢出抛出错误
  • 如果由于精度限制不能表示浮点数或者复数进行舍入
这些要求同时作用于字面量常量额和常量表达式的结果。
变量

变量是一个用来储存值的位置。根据不同的变量类型,可以保存不同的值。
变量声明,函数参数和返回值,声明的函数签名,函数字面值都会为命名变量预留储存空间。调用内置的  new  函数或获取复合字面值的地址都会在运行时为变量分配存储空间。这种匿名变量是通过(可能是隐式的)指针间接引用的。
像数组,切片和结构体类型的变量,它们内部都包含很多元素或字段,而且这些元素和字段都可以直接被访问。数组和切片中的每个元素的行为和单独的变量基本相同。
变量的静态类型可以通过变量声明、提供给 new 的类型、复合字面值、结构体变量声明的元素类型以上几种方式确定。通过new或者类型初始化。接口类型的变量也有一个明确的动态类型,这个动态类型是在运行时赋值给变量的具体值类型(特例:预声明的 nil 是无类型的)。动态类型在程序的执行过程中可能并不相同,但是接口变量的值是可以分配给相同静态类型的变量。
  1. var x interface{}  // x 的静态类型为 interface{} 值为 nil
  2. var v *T           // v 的静态类型为 *T 值为 nil
  3. x = 42             // x 的动态类型为 int 值为 42
  4. x = v              // x 动态类型为 *T 值为 (*T)(nil)
复制代码
在表达式中使用变量可以取出变量的值;这个值就是变量最近一次被赋予的值。如果没有对变量赋过值,那么他的值是该类型的零值。
类型

类型是一个集合,集合包括值和针对值的操作&方法。一个类型可以使用类型名来表示。类型有多种表现形式:如果存在类型名,可以使用类型名表示,或者也可以使用根据已有类型组合成的类型字面值。
  1. Type      = TypeName | TypeLit | "(" Type ")" .
  2. TypeName  = identifier | QualifiedIdent .
  3. TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
  4.             SliceType | MapType | ChannelType .
复制代码
Go 已经预先声明了某些类型的名称。并引入了类型声明。复合类型(数组、结构体、指针、函数、接口、切片、map、channel)可以使用他们的类型字面值。
每个类型T都有一个底层类型。如果T是预定义类型或者类型字面值。那么底层类型就是他自身。否则,T的底层类型就是它再类型声明时引用到的类型。
  1. type (
  2.         A1 = string
  3.         A2 = A1
  4. )
  5. type (
  6.         B1 string
  7.         B2 B1
  8.         B3 []B1
  9.         B4 B3
  10. )
复制代码
string,A1,A2,B1,B2 的底层类型是 string。[]B1,B3,B4 的下游类型是[]B1。
方法集

类型可能会有一个与之关联的方法集。接口类型的方法集就可以使用自身表示。对于其他类型,类型 T 的方法集由所有接收者类型为 T 的方法组成。而对应指针类型 *T 的方法集由所有接收者类型为 T 或 *T 的方法组成。如果是结构体类型且含有嵌入字段,那么方法集中可能还会包含更多的方法,具体请看结构体类型章节。其他类型的方法集都为空。方法集中的每个方法都有唯一且不为空的方法名。
类型的方法集用来确定类型实现的接口和以类型作为接收者能够调用的方法。
布尔类型

布尔类型表示预定义常量 true 和 false 表示布尔真实值的集合。预定义的布尔类型为 bool;它是通过类型声明创建的。
数字类型

一个数字类型相当于整型和浮点型的所有值的集合。预定义的数字类型包括:
  1. uint8       8 位无符号整数集合 (0 to 255)
  2. uint16      16 位无符号整数集合 (0 to 65535)
  3. uint32      32 位无符号整数集合 (0 to 4294967295)
  4. uint64      64 位无符号整数集合 (0 to 18446744073709551615)
  5. int8        8 位有符号整数集合 (-128 to 127)
  6. int16       16 位有符号整数集合 (-32768 to 32767)
  7. int32       32 位有符号整数集合 (-2147483648 to 2147483647)
  8. int64       64 位有符号整数集合 (-9223372036854775808 to 9223372036854775807)
  9. float32     IEEE-754 32 位浮点数集合
  10. float64     IEEE-754 64 位浮点数集合
  11. complex64   实部虚部都为 float32 的复数集合
  12. complex128  实部虚部都为 float64 的复数集合
  13. byte        uint8 的别名
  14. rune        int32 的别名
复制代码
n 位整数的值具有 n 比特的宽度并用补码表示。
以下几种预定义类型由具体平台实现指定长度:
  1. uint     32 或 64 位
  2. int      和 uint 位数相同
  3. uintptr  能够容纳指针值的无符号整数
复制代码
为了避免移植性问题,除了被 uint8 的别名 byte 和 int32 的别名 rune,其他所有的数字类型都是通过类型声明定义。当在表达式中使用不同的数字类型需要进行类型转换。例如:int32 和 int 不是相同的类型,即使他们在指定的平台上是相等的。
字符串类型

字符串类型表示字符串的值类型。字符串的值是一个字节序列(有可能为空)。字符串一旦创建就无法修改它的值。预定义的字符串类型是 string,它是通过类型声明定义的。
可以使用内置函数 len 获取字符串长度。如果字符串是常量那么它的长度在编译时也为常量。可以通过数字下标 0~len(s)-1 访问字符串字节。获取字符串的地址是非法操作;如果 s 是字符串的第 i 个字节,那么 &s 是无效的。
数组类型

数组是一定数量的单一类型元素序列,而这个单一类型叫做元素类型。元素的个数表示元素的长度,它永远不是负数。
  1. ArrayType   = "[" ArrayLength "]" ElementType .
  2. ArrayLength = Expression .
  3. ElementType = Type .
复制代码
长度是数组类型的一部分;它是一个类型为 int 的非负常量。可以用内置函数 len 获取数组的长度。元素可以通过下标 0~len(a)-1 访问。数组一般都是一维的,不过也可以是多维的。
  1. [32]byte
  2. [2*N] struct { x, y int32 }
  3. [1000]*float64
  4. [3][5]int
  5. [2][2][2]float64  // same as [2]([2]([2]float64))
复制代码
切片类型

切片描述了底层数组的一个连续片段并提供对连续片段内元素的访问。切片类型表示元素类型的数组的所有切片的集合。没有被初始化的切片用 nil 表示。
  1. SliceType = "[" "]" ElementType .
复制代码
与数组一样,切片的可以使用索引访问并且有长度,切片的长度可以通过内置的 len 函数获取;与数组不同的是它的长度在运行时是可以变化的。我们可以通过下标 0~len(s)-1 来访问切片内的元素。切片的索引可能会小于相同元素再底层数组的索引。
切片一旦初始化,那么就有一个与之对应的底层数组保存切片中的元素。切片和底层的数组还有其他指向该数组的切片共享相同的储存空间;而不同的数组总是有着不同的存储空间。
切片的底层数组可能会延伸到切片末尾以外,切片的容积等于切片现在的长度加上数组中切片还没使用的长度;可以从原始切片中切出一个长度与容量相等的切片。切片的容量可以通过内置的 cap(a) 函数来获取。可以通过函数make来创建一个T类型的新切片。
使用内置函数 make 可以出实话给定元素类型 T 的切片。make 函数接收三个参数:切片类型、切片长度、切片容积,其中切片容积是可选参数。make 创建的切片会在底层分配一个切片所引用的新数组。
  1. make([]T, length, capacity)
复制代码
make 的作用就是创建新数组并切分它,所以下面两种写法是等价的:
  1. make([]int, 50, 100)
  2. new([100]int)[0:50]
复制代码
与数组相同,切片一般是一维的,不过也可以复合成多维。数组中的数组都必须是相同的长度,但是切片中的切片长度是动态变化的,不过切片中的切片需要单独初始化。
结构体类型

结构体是一个命名元素序列,命名元素也叫做字段,每个字段都对应一个名称和类型,字段的名字可以是显式指定的(标识符列表)也可以是隐式的(嵌入字段)。在结构体中非空字段具有唯一性。
  1. StructType    = "struct" "{" { FieldDecl ";" } "}" .
  2. FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
  3. EmbeddedField = [ "*" ] TypeName .
  4. Tag           = string_lit .
复制代码
  1. // 空结构体.
  2. struct {}
  3. // 6个字段的结构体.
  4. struct {
  5.         x, y int
  6.         u float32
  7.         _ float32  // padding
  8.         A *[]int
  9.         F func()
  10. }
复制代码
一个指定了类型而没有指定名称的字段叫做嵌入字段,嵌入字段必须指定类型名 T 或指向非接口类型的指针类型 *T,其中 T 不能为指针类型。或者一个非接口类型的指针。并且T本身不能为指针类型。这种情况下会把类型名作为字段的名字。
  1. // 一个包含 4 个嵌入字段 T1, *T2, P.T3 和 *P.T4 的结构体
  2. struct {
  3.         T1        // 字段名为 T1
  4.         *T2       // 字段名为 T2
  5.         P.T3      // 字段名为 T3
  6.         *P.T4     // 字段名为 T4
  7.         x, y int  // 字段名为 x 和 y
  8. }
复制代码
以下声明是错误的因为字段名称必须唯一。
  1. struct {
  2.         T     // 嵌入字段 *T 与 *P.T 冲突
  3.         *T    // 嵌入字段 T 与 *P.T 冲突
  4.         *P.T  // 嵌入字段 T 与 *T 冲突
  5. }
复制代码
如果 x.f 是表示该字段或方法 f 的合法选择器,则会调用结构 x 中嵌入字段的字段或方法 f。
从嵌入字段组合来的字段与结构体原来的字段行为基本相同,只是不能在结构体的复合字面值中直接使用。
给定一个结构体 S 和一个类型 T,依据以下规则生成组合后的方法集:

  • 如果 S 包含嵌入字段 T,则 S 和 *S 的方法集包括接收者为 T 的方法集,而 *S 包括 接收者为 *T 的方法集。
  • 如果 S 包含字段 T。那么S和S均包含接收者为 T 和 *T 的所有方法集。
声明字段时可以给该字段添加一个字符串的 tag。这个 tag 将会成为它所对应字段的一个属性。空 tag 和缺省 tag 是相同的。tag 的值可以通过反射的接口获取,可以作为类型结构体的类型定义的一部分,也可以忽略。
  1. struct {
  2.         x, y float64 ""  // 空 tag 和缺省 tag 相同
  3.         name string  "any string is permitted as a tag"
  4.         _    [4]byte "ceci n'est pas un champ de structure"
  5. }
  6. // 结构体对应一个 TimeStamp 的 protocol buffer.
  7. // tag 字符串中定义了 protocol buffer 字段对应的数字;
  8. // 一般使用 reflect 包读取他们.
  9. struct {
  10.         microsec  uint64 `protobuf:"1"`
  11.         serverIP6 uint64 `protobuf:"2"`
  12. }
复制代码
指针类型

指针类型表示所有指向给定类型变量的指针集合。这个指定的类型叫做指针的基础类型。没有初始化的指针值为nil。
  1. PointerType = "*" BaseType .
  2. BaseType    = Type .
复制代码
  1. *Point
  2. *[4]int
复制代码
函数类型

函数类型可以表示所有具有相同参数类型和返回值类型的函数。未初始化的函数类型值为 nil。
  1. FunctionType   = "func" Signature .
  2. Signature      = Parameters [ Result ] .
  3. Result         = Parameters | Type .
  4. Parameters     = "(" [ ParameterList [ "," ] ] ")" .
  5. ParameterList  = ParameterDecl { "," ParameterDecl } .
  6. ParameterDecl  = [ IdentifierList ] [ "..." ] Type .
复制代码
在参数和返回值列表中,标识符列表必须同时存在或缺省。如果存在,那么每个名字都表示指定类型的一个参数/返回值,这些标识符必须非空并且不能重复。如果缺省,指定类型的参数/返回值使用对应的类型表示。参数列表和返回值列表一般都是需要加括号,不过在只有一个缺省返回值时,它可以不使用括号。
函数的最后一个参数可以添加前缀 ...。包含这种参数的函数叫做变参函数,它可以接收零个或多个参数。
  1. func()
  2. func(x int) int
  3. func(a, _ int, z float32) bool
  4. func(a, b int, z float32) (bool)
  5. func(prefix string, values ...int)
  6. func(a, b int, z float64, opt ...interface{}) (success bool)
  7. func(int, int, float64) (float64, *[]int)
  8. func(n int) func(p *T)
复制代码
接口类型

接口类型指定了一个方法集。一个接口类型变量可以保存任何方法集是该接口超集的类型。我们可以认为类型实现了接口。没有初始化的接口类型值为 nil。
  1. InterfaceType      = "interface" "{" { MethodSpec ";" } "}" .
  2. MethodSpec         = MethodName Signature | InterfaceTypeName .
  3. MethodName         = identifier .
  4. InterfaceTypeName  = TypeName .
复制代码
在接口类型的方法集中,每个方法的名称必须是非空且唯一。
  1. // A simple File interface
  2. interface {
  3.         Read(b Buffer) bool
  4.         Write(b Buffer) bool
  5.         Close()
  6. }
复制代码
接口可以由多个类型实现,例如:类型 S1 和类型 S2 都有以下方法集:
  1. func (p T) Read(b Buffer) bool { return … }
  2. func (p T) Write(b Buffer) bool { return … }
  3. func (p T) Close() { … }
复制代码
(这里的类型 T 可以表示 S1 也可以表示 S2 ) S1 和 S2 都实现了接口 File,而不用管类型是否还有其他方法。
一个类型实现了任何方法集的为其子集的接口。因此它可能实现了多个不同接口。例如:所有的类型都实现了空接口:
  1. interface{}
复制代码
与之相似,思考下面这个定义为 Locker 的接口:
  1. type Locker interface {
  2.         Lock()
  3.         Unlock()
  4. }
复制代码
如果 S1 和 S2 也实现了它:
  1. func (p T) Lock() { … }
  2. func (p T) Unlock() { … }
复制代码
那它们就实现了两个接口 Locker 和 File。
一个接口 T 可以使用另一个接口 E 来指定方法。这种方式叫做将接口 E 嵌入进接口 T。它把 E 中所有的方法(包括导出和未导出的方法)全部添加进接口 T。
  1. type ReadWriter interface {
  2.         Read(b Buffer) bool
  3.         Write(b Buffer) bool
  4. }
  5. type File interface {
  6.         ReadWriter  // 与添加 ReadWriter 接口中的方法是等价的
  7.         Locker      // 与添加 Locker 接口中的方法是等价的
  8.         Close()
  9. }
  10. type LockedFile interface {
  11.         Locker
  12.         File        // 无效: Lock, Unlock 不是唯一的
  13.         Lock()      // 无效: Lock 不是唯一的
  14. }
复制代码
接口 T 不能递归的嵌入进自己或已经嵌入过它的接口。
  1. // 无效: Bad 不能嵌入它自己
  2. type Bad interface {
  3.         Bad
  4. }
  5. // 无效: Bad1 不能嵌入已经引用它的 Bad2
  6. type Bad1 interface {
  7.         Bad2
  8. }
  9. type Bad2 interface {
  10.         Bad1
  11. }
复制代码
Map类型

map 类型是一种以唯一值作为键的无序集合。
  1. MapType     = "map" "[" KeyType "]" ElementType .
  2. KeyType     = Type .
复制代码
map的键类型必须能使用比较运算符 == 和 != 进行比较。因此它的键类型不能是函数,map,或者切片。如果键是接口类型,那么比较运算符必须能比较他的动态值。如果不能会抛出一个运行时错误。
  1. map[string]int
  2. map[*T]struct{ x, y float64 }
  3. map[string]interface{}
复制代码
map中元素的个数叫做它的长度。对于一个map m。它的长度可以通过内置函数 len 获得,而且它的长度可能再运行时发生变化。map 可以再运行时添加和取回元素,页可以使用内置函数 delete移除元素。
可以使用内置函数 make 初始化一个新的且为空的 map。它能指定 map 的类型和预留的空间:
  1. make(map[string]int)
  2. make(map[string]int, 100)
复制代码
map 的预留空间不会固定住 map 的长度;它可以通过添加一定数量的元素来增加自己的长度(nil map 不能添加元素)。nil map 和空 map 是相等的,只是 nil map 不能添加元素。
Channel类型

channel提供一种手段在并发执行的函数间发送和接收指定类型的值。没有初始化的 channel 是nil。
[code]ChannelType = ( "chan" | "chan" "
您需要登录后才可以回帖 登录 | 立即注册