{"id":23786852,"url":"https://github.com/saberuster/go-language-specification","last_synced_at":"2025-09-06T04:31:16.153Z","repository":{"id":144490880,"uuid":"115570199","full_name":"saberuster/Go-Language-Specification","owner":"saberuster","description":"Golang官网语言规范指南译本","archived":false,"fork":false,"pushed_at":"2018-07-12T00:00:28.000Z","size":74,"stargazers_count":30,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-06T20:51:36.233Z","etag":null,"topics":["documentation","go","golang"],"latest_commit_sha":null,"homepage":"https://golang.org/ref/spec","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saberuster.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-28T01:03:16.000Z","updated_at":"2024-07-17T03:13:59.000Z","dependencies_parsed_at":null,"dependency_job_id":"bc6d872f-d21b-4dea-a371-0c8e788aa464","html_url":"https://github.com/saberuster/Go-Language-Specification","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/saberuster/Go-Language-Specification","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saberuster%2FGo-Language-Specification","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saberuster%2FGo-Language-Specification/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saberuster%2FGo-Language-Specification/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saberuster%2FGo-Language-Specification/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saberuster","download_url":"https://codeload.github.com/saberuster/Go-Language-Specification/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saberuster%2FGo-Language-Specification/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273858578,"owners_count":25180763,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["documentation","go","golang"],"created_at":"2025-01-01T14:27:58.346Z","updated_at":"2025-09-06T04:31:16.088Z","avatar_url":"https://github.com/saberuster.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"#  Go 语言参考手册\n\n官网 [Spec](https://golang.org/ref/spec) 译文\n\n版本：May 9, 2018\n\n## 译者序\n\n\n\n## 目录\n\n[介绍](#user-content-介绍)\n\n[标记](#user-content-标记)\n\n[源码表示法](#源码表示法)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[字符](#user-content-字符)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[字母和数字](#字母和数字)\n\n[词汇元素](#user-content-词汇元素)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[注释](#user-content-注释)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[词汇元素](#user-content-词汇元素)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[分号](#user-content-分号)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[标识符](#user-content-标识符)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[关键字](#user-content-关键字)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[操作符和标点符号](#user-content-操作符和标点符号)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[整型字面值](#user-content-整型字面值)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[浮点字面值](#user-content-浮点字面值)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[虚数字面值](#user-content-虚数字面值)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[Rune 字面值](#user-content-Rune-字面值)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[字符串字面量](#user-content-字符串字面量)\n\n[常量](#user-content-常量)\n\n[变量](#user-content-变量)\n\n[类型](#user-content-类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[方法集](#user-content-方法集)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[布尔类型](#user-content-布尔类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[数字类型](#user-content-数字类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[字符串类型](#user-content-字符串类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[数组类型](#user-content-数组类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[切片类型](#user-content-切片类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[结构体类型](#user-content-结构体类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[指针类型](#user-content-指针类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[函数类型](#user-content-函数类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[接口类型](#user-content-接口类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[Map类型](#user-content-Map类型)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[Channel类型](#user-content-Channel类型)\n\n[类型的属性和值](#user-content-类型的属性和值)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[类型标识](#user-content-类型标识)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[可分配性](#user-content-可分配性)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[代表性](#user-content-代表性)\n\n[代码块](#user-content-代码块)\n\n[声明和作用域](#user-content-声明和作用域)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[标签的作用域](#user-content-标签的作用域)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[空标识符](#user-content-空标识符)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[预定义的标识符](#user-content-预定义的标识符)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[导出标识符](#user-content-导出标识符)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[标识符的唯一性](#user-content-标识符的唯一性)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[常量声明](#user-content-常量声明)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[Iota](#user-content-Iota)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[类型声明](#user-content-类型声明)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[变量声明](#user-content-变量声明)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[短变量声明](#user-content-短变量声明)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[函数声明](#user-content-函数声明)\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;[方法声明](#user-content-方法声明)\n\n## 介绍\n\n这是一个 Go 语言的参考手册，你也可以访问[golang.org](https://golang.org/)获取更多信息和其他文档。\n\nGo 是在设计时考虑了系统编程的通用型编程语言。它是强类型，有垃圾回收机制并原生支持并发编程。Go 程序由一个或多个 package 组成，这样可以高效的管理依赖。\n\nGo 的语法简洁且有规则，这让自动化工具可以很容易的分析代码，例如：集成开发环境。\n\n## 标记\n\n语法采用扩展巴科斯范式。\n\n```\nProduction  = production_name \"=\" [ Expression ] \".\" .\nExpression  = Alternative { \"|\" Alternative } .\nAlternative = Term { Term } .\nTerm        = production_name | token [ \"…\" token ] | Group | Option | Repetition .\nGroup       = \"(\" Expression \")\" .\nOption      = \"[\" Expression \"]\" .\nRepetition  = \"{\" Expression \"}\" .\n```\n\n产生式是由词法单元和以下操作符构成的表达式（优先级依次递增）：\n\n```\n|   或\n()  分组\n[]  可选 (出现 0 或 1 次)\n{}  可重复 (出现 0 到 n 次)\n```\n\n小写的产生式名称用来与词法单元区分。非终结符采用驼峰式。词法单元由双引号或反引号组成。\n\n`a...b` 表示从 `a` 到 `b` 之间的任意字符。省略号 `...` 也可以在规范中表示对更详细的枚举和代码片段的省略。字符 `...` 不是 Go 语言的词法单元。\n\n## 源码表示法\n\nGo 的源代码使用 UTF-8 编码的 Unicode 文本。不过它并不是完全规范化的，单重音的代码点与由相同字符和音标组成的代码点是不同的；前者我们认为它是两个代码点。简单来讲，文档会在源代码文本中使用非规范的术语字符来表示一个 Unicode 代码点。\n\n每个代码点都是不同的；相同字符的大写和小写形式表示不同的字符。\n\n实现限制：为了兼容其他工具，编译器不允许出现 Utf-8 编码的源文本中的 NUL 字符（U+0000）。\n\n实现限制：为了兼容其他工具，如果源文本中是以Utf-8 编码的字节序标记（U+FEFF）为起始代码点。编译器会忽略它。字节序标记不应出现在源文本的任何位置。\n\n#### 字符\n\n这些单词表示 Unicode 字符的类别：\n\n```\nnewline        = /* Unicode 代码点 U+000A */ .\nunicode_char   = /* 排除换行以外的任意 Unicode 代码点 */ .\nunicode_letter = /* 一个字母（\"Letter\"）类型的 Unicode 代码点  */ .\nunicode_digit  = /* 一个数字（\"Number, decimal digit\"）类型的 Unicode 代码点  */ .\n```\n\n在 Unicode8.0 标准中，第 4.5 章节 “一般类别” 中定义了字符的类别。Go 能够处理任何字符集，包括 Lu，Li，Lt，Lm 或 Lo 作为 Unicode 字母，还可以把数字字符集 Nd 当作 Unicode 数字处理。\n\n#### 字母和数字\n\n我们认为下划线 `_` （U+005F）是一个字母：\n\n```\nletter        = unicode_letter | \"_\" .\ndecimal_digit = \"0\" … \"9\" .\noctal_digit   = \"0\" … \"7\" .\nhex_digit     = \"0\" … \"9\" | \"A\" … \"F\" | \"a\" … \"f\" .\n```\n\n## 词汇元素\n\n#### 注释\n\n注释是程序的说明文档。在 Go 中有两种形式：\n\n* 单行注释从 `//` 开始直到行末结束。\n* 通用注释从 `/*` 开始直到 `*/` 结束。\n\n注释不能嵌套在其他注释、字符串和 rune 的字面值中。不包含换行符的通用注释之间通过空格符连接，其他情况下每段注释都会另起一行。\n\n#### 词汇元素\n\n词汇元素构成了 Go 语言的词汇表。它有四种类型：标识符、关键字、操作符/标点符号、字面值。空白符可以是空格（U+0020）、水平制表符（U+0009）、换行符（U+000D）或换行符（U+000A）。它本身会被忽略，一般用来区分不同的词汇元素。换行符或文件终止符（EOF）还可能触发编译程序在源代码的行末或文件末尾追加分号。在分解源代码的词汇元素的过程中，会把当前可以形成有效词汇元素的最长字符序列作为下一个词汇元素。\n\n#### 分号\n\n正规语法在很多产生式中使用分号 \";\" 作为终结符。Go 程序中遵循下面两条规则省略了大部分的分号：\n\n1. 当某行的最后一个词汇元素是以下元素时自动补全分号：\n   - 一个标识符。\n   - 一个整数，浮点数，虚数，rune 或字符串字面值。\n   - 关键字 `break`、`continue`、`fallthrough` 和 `return` 其中之一。\n   - 操作符/标点符号 `++`，`--`，`)`，`]` 和 `}` 其中之一。\n2. 为了支持独占一行的复杂语句，会省略与 \")\" 或 \"}\" 相邻的分号。\n\n为了反应惯用用途，本篇文档的所有例子都基于以上规则省略分号。\n\n#### 标识符\n\n标识符表示程序实体单元，例如：变量、类型。一个标识符由一个或多个字母和数字组成。标识符的首字符必须为字母。\n\n```\nidentifier = letter { letter | unicode_digit } .\n```\n\n```\na\n_x9\nThisVariableIsExported\nαβ\n```\n\nGo 已经预定义了一些标识符。\n\n#### 关键字\n\n以下关键字是预留的，它们不能作为标识符：\n\n```\nbreak        default      func         interface    select\ncase         defer        go           map          struct\nchan         else         goto         package      switch\nconst        fallthrough  if           range        type\ncontinue     for          import       return       var\n```\n\n#### 操作符和标点符号\n\n以下字符序列用于表示操作符（包括赋值运算符）和标点符号：\n\n```\n+    \u0026     +=    \u0026=     \u0026\u0026    ==    !=    (    )\n-    |     -=    |=     ||    \u003c     \u003c=    [    ]\n*    ^     *=    ^=     \u003c-    \u003e     \u003e=    {    }\n/    \u003c\u003c    /=    \u003c\u003c=    ++    =     :=    ,    ;\n%    \u003e\u003e    %=    \u003e\u003e=    --    !     ...   .    :\n     \u0026^          \u0026^=\n```\n\n#### 整型字面值\n\n整型字面值是一个数字序列，相当于整型常量。可以使用前缀指定非小数进制：0 表示八进制，0x/0X 表示十六进制。在十六进制字面值中，字母 a-f 和 A-F 都表示数字 10-15。\n\n```\nint_lit     = decimal_lit | octal_lit | hex_lit .\ndecimal_lit = ( \"1\" … \"9\" ) { decimal_digit } .\noctal_lit   = \"0\" { octal_digit } .\nhex_lit     = \"0\" ( \"x\" | \"X\" ) hex_digit { hex_digit } .\n```\n\n```\n42\n0600\n0xBadFace\n170141183460469231731687303715884105727\n```\n\n#### 浮点字面值\n\n浮点字面值是一个小数，相当于浮点数常量。它由整数部分，小数点，小数部分和指数部分构成。整数部分和小数部分用小数点链接；指数部分由  `e` / `E` 字符后接一个有符号指数构成。整数部分和小数部分可以省略其一；小数点和指数部分可以省略其一。\n\n```\nfloat_lit = decimals \".\" [ decimals ] [ exponent ] |\n            decimals exponent |\n            \".\" decimals [ exponent ] .\ndecimals  = decimal_digit { decimal_digit } .\nexponent  = ( \"e\" | \"E\" ) [ \"+\" | \"-\" ] decimals .\n```\n\n```\n0.\n72.40\n072.40  // == 72.40\n2.71828\n1.e+0\n6.67428e-11\n1E6\n.25\n.12345E+5\n```\n\n#### 虚数字面值\n\n虚数字面值是一个小数，相当于复数常量中的虚数部分。它由浮点数或者整数后接小写字母 i 构成。\n\n```\nimaginary_lit = (decimals | float_lit) \"i\" .\n```\n\n```\n0i\n011i  // == 11i\n0.i\n2.71828i\n1.e+0i\n6.67428e-11i\n1E6i\n.25i\n.12345E+5i\n```\n\n#### Rune 字面值\n\nrune 类型字面值相当于一个 rune 常量。它是一个表示 Unicode 代码点的整数。rune 类型字面值表示为用单引号包裹的一个或多个字符，像 'x' 或 '\\n'。在单引号中除了换行符和未转义的单引号其他的字符都可以直接显示。单引号包裹的字符的值和字符在 Unicode 编码中的值相等，而以反斜线开头的多字符序列会把值翻译成多种格式。\n\n使用引号表示单字符是最简单的方式；因为 Go 的源文本是 UTF-8 编码，一个整数可能代表多个 UTF-8 字节。例如， 'a' 可以使用单字节表示字符 a，Unicode 编码 U+0061，值 0x61，而 'ä' 是两字节表示分音符的 a，Unicode 编码 U+00E4，值 0xe4。\n\n反斜线能将任意值编码成 ASCII 文本。有四种方式将整数值表示为数字常量：`\\x` 后接两个十六进制数；`\\u` 后接四个十六进制数；`\\U` 后接八个十六进制数。 `\\` 后接三个八进制数。每种情况下都使用相应进制来表示字面量的整数值。\n\n虽然这四种方式都以整数表示，但它们的有效区间并不相同。八进制只能表示 0 - 255 以内的整数。十六进制满可以满足需求。`\\u` 和 `\\U`  都可以表示 Unicode 代码点，不过其中的一些值是无效的，特别是 0x10FFFF 以上的值。\n\n反斜线结合以下字符具有特殊含义：\n\n```\n\\a   U+0007 alert or bell\n\\b   U+0008 退格符\n\\f   U+000C form feed\n\\n   U+000A line feed or newline\n\\r   U+000D carriage return\n\\t   U+0009 水平制表符\n\\v   U+000b 垂直制表符\n\\\\   U+005c 反斜线\n\\'   U+0027 单引号  (只在 rune 字面值中有效)\n\\\"   U+0022 双引号  (只在字符串字面值中有效)\n```\n\n其他所有以反斜线开头的序列在 rune 的规则中都是非法的。\n\n```\nrune_lit         = \"'\" ( unicode_value | byte_value ) \"'\" .\nunicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .\nbyte_value       = octal_byte_value | hex_byte_value .\noctal_byte_value = `\\` octal_digit octal_digit octal_digit .\nhex_byte_value   = `\\` \"x\" hex_digit hex_digit .\nlittle_u_value   = `\\` \"u\" hex_digit hex_digit hex_digit hex_digit .\nbig_u_value      = `\\` \"U\" hex_digit hex_digit hex_digit hex_digit\n                           hex_digit hex_digit hex_digit hex_digit .\nescaped_char     = `\\` ( \"a\" | \"b\" | \"f\" | \"n\" | \"r\" | \"t\" | \"v\" | `\\` | \"'\" | `\"` ) .\n```\n\n```\n'a'\n'ä'\n'本'\n'\\t'\n'\\000'\n'\\007'\n'\\377'\n'\\x07'\n'\\xff'\n'\\u12e4'\n'\\U00101234'\n'\\''         // 包含单引号的 rune 字面值\n'aa'         // 无效: 太多字符\n'\\xa'        // 无效: 缺少十六进制数\n'\\0'         // 无效: 缺少八进制数\n'\\uDFFF'     // 无效: surrogate half\n'\\U00110000' // 无效: 非法的 Unicode 代码点\n```\n\n#### 字符串字面量\n\n字符串字面量表示从字符序列中获取的字符串常量。它有两种格式：原始字符串字面量和解释型字符串字面量。\n\n原始字符串是由反引号包裹（\\`foo\\`）。字符串中除反引号以外的其他字符都会显示出来。原生字符串由反引号之间的（默认 UTF-8 编码）的字符组成。它的值为引号内未经解释（默认 UTF-8 编码）所有字符；尤其是，反斜线再字符串中没有特殊意义并且字符串中保留换行符。在原始字符串的值中会丢弃回车键返回 '\\r' 字符。\n\n解释型字符串由双引号之间的字符组成（\"bar\"）。除了换行符和双引号其他字符都会显示出来。双引号之间的文本组成字面量的值。反斜线的转义规则与 rune 字面量基本相同（不同的是 \\’ 非法，而 \\\\\\\" 合法）。三位八进制数（\\nnn）和两位十六进制数（\\xnn）换码符的值表示相应字符串的字节。其他的换码符都表示字符各自的 UTF-8 编码（可能是多字节）。因此字符串 \\377 和 \\xFF 都表示值为 0xFF=255 的单个字节，而  `ÿ`, `\\u00FF`, `\\U000000FF` 和 `\\xc3\\xbf` 表示 UTF-8 编码字符 U+00FF 的两个字节 0xc3 0xbf。 \n\n```\nstring_lit             = raw_string_lit | interpreted_string_lit .\nraw_string_lit         = \"`\" { unicode_char | newline } \"`\" .\ninterpreted_string_lit = `\"` { unicode_value | byte_value } `\"` .\n```\n\n```\n`abc`                // 等价于 \"abc\"\n`\\n\n\\n`                  // 等价于 \"\\\\n\\n\\\\n\"\n\"\\n\"\n\"\\\"\"                 // 等价于 `\"`\n\"Hello, world!\\n\"\n\"日本語\"\n\"\\u65e5本\\U00008a9e\"\n\"\\xff\\u00FF\"\n\"\\uD800\"             // 无效: surrogate half\n\"\\U00110000\"         // 无效: 无效的 Unicode 代码点\n```\n\n这些例子都表示相同的字符串：\n\n```\n\"日本語\"                                 // UTF-8 文本\n`日本語`                                 // UTF-8 文本作为原生字面值\n\"\\u65e5\\u672c\\u8a9e\"                    // 确定的 Unicode 代码点 \n\"\\U000065e5\\U0000672c\\U00008a9e\"        // 确定的 Unicode 代码点\n\"\\xe6\\x97\\xa5\\xe6\\x9c\\xac\\xe8\\xaa\\x9e\"  // 确定的 UTF-8 字节\n```\n\n如果源代码中使用两个代码点表示一个字符，例如带音标的字母，把它放在 rune 中会报错（它不是单代码点）。并且在字符串中会显示两个代码点。\n\n## 常量\n\n常量分为：布尔型，rune型，整型，浮点型，复数型，字符串型。其中 rune，整型，浮点型，复数型统称为数字常量。\n\n常量的值可以表示为一个 rune字面量，整数字面量，浮点数字面量，虚数字面量，字符串字面量，表示常量的标识符，常量表达式，一个转换结果为常量的类型转换，和一些返回值为常量的内置函数(接受任何值的`unsafe.Sizeof`，接受部分表达式的`cap` 或 `len`，接受虚数常量的`real` 和 `imag`，接受数字常量的 `complex`)。布尔类型的值为预定义常量 `true` 或 `false`，预定义的标识符 `iota` 表示一个整型常量。\n\n一般情况下复数常量是常量表达式的一种形式。会在常量表达式章节详细讨论。\n\n数字常量可以表示任意精度的确定值而且不会溢出。因此，没有常量可以表示非 0，无穷大和非数字值。\n\n常量可以指定类型也可以不指定类型。字面值常量，`true`，`false`，`iota`，和只包含无类型常量操作的常量表达式是无类型的。\n\n常量可以通过常量声明和转换时显式的指定具体类型，也可以隐式的在变量声明、赋值或作为表达式操作元时隐式的指定具体类型。如果常量的值和他的类型不匹配，会报错。\n\n无类型常量由一个默认的类型，这个类型会根据使用常量时的上下文进行隐式转换。例如：短变量声明 `i := 0` 没有指定 i 的类型。无类型常量的默认类型可以是：`bool`，`rune`，`int`，`float64`，`complex128` 或者 `string`，具体选择哪种类型由常量的值决定。\n\n实现限制：虽然数字常量在 Go 中是任意精度，不过编译器在实现时会在内部限制精度。这意味着每个编译器实现都要：\n\n- 至少保证整形常量有 256 位\n- 浮点数常量（包括复数常量）都要保证至少 256 位的主体部分和至少 16 位的有符号指数部分\n- 如果不能表示给定整数的精度抛出错误\n- 如果浮点数或复数溢出抛出错误\n- 如果由于精度限制不能表示浮点数或者复数进行舍入\n\n这些要求同时作用于字面量常量额和常量表达式的结果。\n\n## 变量\n\n变量是一个用来储存值的位置。根据不同的变量类型，可以保存不同的值。\n\n变量声明，函数参数和返回值，声明的函数签名，函数字面值都会为命名变量预留储存空间。调用内置的  `new`  函数或获取复合字面值的地址都会在运行时为变量分配存储空间。这种匿名变量是通过（可能是隐式的）指针间接引用的。\n\n像数组，切片和结构体类型的变量，它们内部都包含很多元素或字段，而且这些元素和字段都可以直接被访问。数组和切片中的每个元素的行为和单独的变量基本相同。\n\n变量的静态类型可以通过变量声明、提供给 `new` 的类型、复合字面值、结构体变量声明的元素类型以上几种方式确定。通过new或者类型初始化。接口类型的变量也有一个明确的动态类型，这个动态类型是在运行时赋值给变量的具体值类型（特例：预声明的 nil 是无类型的）。动态类型在程序的执行过程中可能并不相同，但是接口变量的值是可以分配给相同静态类型的变量。\n\n```\nvar x interface{}  // x 的静态类型为 interface{} 值为 nil\nvar v *T           // v 的静态类型为 *T 值为 nil\nx = 42             // x 的动态类型为 int 值为 42\nx = v              // x 动态类型为 *T 值为 (*T)(nil)\n```\n\n在表达式中使用变量可以取出变量的值；这个值就是变量最近一次被赋予的值。如果没有对变量赋过值，那么他的值是该类型的零值。\n\n## 类型\n\n类型是一个集合，集合包括值和针对值的操作\u0026方法。一个类型可以使用类型名来表示。类型有多种表现形式：如果存在类型名，可以使用类型名表示，或者也可以使用根据已有类型组合成的类型字面值。\n\n```\nType      = TypeName | TypeLit | \"(\" Type \")\" .\nTypeName  = identifier | QualifiedIdent .\nTypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |\n\t    SliceType | MapType | ChannelType .\n```\n\nGo 已经预先声明了某些类型的名称。并引入了类型声明。复合类型（数组、结构体、指针、函数、接口、切片、map、channel）可以使用他们的类型字面值。\n\n每个类型T都有一个底层类型。如果T是预定义类型或者类型字面值。那么底层类型就是他自身。否则，T的底层类型就是它再类型声明时引用到的类型。\n\n```go\ntype (\n\tA1 = string\n\tA2 = A1\n)\n\ntype (\n\tB1 string\n\tB2 B1\n\tB3 []B1\n\tB4 B3\n)\n\n```\n\n`string`，`A1`，`A2`，`B1`，`B2` 的底层类型是 `string`。`[]B1`，`B3`，`B4` 的下游类型是[]B1。\n\n#### 方法集\n\n\n类型可能会有一个与之关联的方法集。接口类型的方法集就可以使用自身表示。对于其他类型，类型 T 的方法集由所有接收者类型为 T 的方法组成。而对应指针类型 \\*T 的方法集由所有接收者类型为 T 或 \\*T 的方法组成。如果是结构体类型且含有嵌入字段，那么方法集中可能还会包含更多的方法，具体请看结构体类型章节。其他类型的方法集都为空。方法集中的每个方法都有唯一且不为空的方法名。\n\n类型的方法集用来确定类型实现的接口和以类型作为接收者能够调用的方法。\n\n#### 布尔类型\n\n布尔类型表示预定义常量 `true` 和 `false` 表示布尔真实值的集合。预定义的布尔类型为 `bool`；它是通过类型声明创建的。\n\n#### 数字类型\n\n一个数字类型相当于整型和浮点型的所有值的集合。预定义的数字类型包括：\n```\nuint8       8 位无符号整数集合 (0 to 255)\nuint16      16 位无符号整数集合 (0 to 65535)\nuint32      32 位无符号整数集合 (0 to 4294967295)\nuint64      64 位无符号整数集合 (0 to 18446744073709551615)\n\nint8        8 位有符号整数集合 (-128 to 127)\nint16       16 位有符号整数集合 (-32768 to 32767)\nint32       32 位有符号整数集合 (-2147483648 to 2147483647)\nint64       64 位有符号整数集合 (-9223372036854775808 to 9223372036854775807)\n\nfloat32     IEEE-754 32 位浮点数集合\nfloat64     IEEE-754 64 位浮点数集合\n\ncomplex64   实部虚部都为 float32 的复数集合\ncomplex128  实部虚部都为 float64 的复数集合\n\nbyte        uint8 的别名\nrune        int32 的别名\n```\n\nn 位整数的值具有 n 比特的宽度并用补码表示。\n\n以下几种预定义类型由具体平台实现指定长度：\n\n```\nuint     32 或 64 位\nint      和 uint 位数相同\nuintptr  能够容纳指针值的无符号整数\n```\n为了避免移植性问题，除了被 uint8 的别名 byte 和 int32 的别名 rune，其他所有的数字类型都是通过类型声明定义。当在表达式中使用不同的数字类型需要进行类型转换。例如：int32 和 int 不是相同的类型，即使他们在指定的平台上是相等的。\n\n#### 字符串类型\n\n字符串类型表示字符串的值类型。字符串的值是一个字节序列（有可能为空）。字符串一旦创建就无法修改它的值。预定义的字符串类型是 `string`，它是通过类型声明定义的。\n\n可以使用内置函数 `len` 获取字符串长度。如果字符串是常量那么它的长度在编译时也为常量。可以通过数字下标 0～len(s)-1 访问字符串字节。获取字符串的地址是非法操作；如果 `s[i]` 是字符串的第 i 个字节，那么 `\u0026s[i]` 是无效的。\n\n#### 数组类型\n\n数组是一定数量的单一类型元素序列，而这个单一类型叫做元素类型。元素的个数表示元素的长度，它永远不是负数。\n\n```\nArrayType   = \"[\" ArrayLength \"]\" ElementType .\nArrayLength = Expression .\nElementType = Type .\n```\n\n长度是数组类型的一部分；它是一个类型为 int 的非负常量。可以用内置函数 `len` 获取数组的长度。元素可以通过下标 `0～len(a)-1` 访问。数组一般都是一维的，不过也可以是多维的。\n\n```\n[32]byte\n[2*N] struct { x, y int32 }\n[1000]*float64\n[3][5]int\n[2][2][2]float64  // same as [2]([2]([2]float64))\n```\n\n#### 切片类型\n\n切片描述了底层数组的一个连续片段并提供对连续片段内元素的访问。切片类型表示元素类型的数组的所有切片的集合。没有被初始化的切片用 nil 表示。\n```\nSliceType = \"[\" \"]\" ElementType .\n```\n\n与数组一样，切片的可以使用索引访问并且有长度，切片的长度可以通过内置的 `len` 函数获取；与数组不同的是它的长度在运行时是可以变化的。我们可以通过下标 `0～len(s)-1` 来访问切片内的元素。切片的索引可能会小于相同元素再底层数组的索引。\n\n切片一旦初始化，那么就有一个与之对应的底层数组保存切片中的元素。切片和底层的数组还有其他指向该数组的切片共享相同的储存空间；而不同的数组总是有着不同的存储空间。\n\n切片的底层数组可能会延伸到切片末尾以外，切片的容积等于切片现在的长度加上数组中切片还没使用的长度；可以从原始切片中切出一个长度与容量相等的切片。切片的容量可以通过内置的 `cap(a)` 函数来获取。可以通过函数`make`来创建一个T类型的新切片。\n\n\n\n使用内置函数 `make` 可以出实话给定元素类型 T 的切片。`make` 函数接收三个参数：切片类型、切片长度、切片容积，其中切片容积是可选参数。`make` 创建的切片会在底层分配一个切片所引用的新数组。\n\n```go\nmake([]T, length, capacity)\n```\n\n`make` 的作用就是创建新数组并切分它，所以下面两种写法是等价的：\n```\nmake([]int, 50, 100)\nnew([100]int)[0:50]\n```\n\n与数组相同，切片一般是一维的，不过也可以复合成多维。数组中的数组都必须是相同的长度，但是切片中的切片长度是动态变化的，不过切片中的切片需要单独初始化。\n\n#### 结构体类型\n\n结构体是一个命名元素序列，命名元素也叫做字段，每个字段都对应一个名称和类型，字段的名字可以是显式指定的（标识符列表）也可以是隐式的（嵌入字段）。在结构体中非空字段具有唯一性。\n\n```\nStructType    = \"struct\" \"{\" { FieldDecl \";\" } \"}\" .\nFieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .\nEmbeddedField = [ \"*\" ] TypeName .\nTag           = string_lit .\n```\n\n```go\n// 空结构体.\nstruct {}\n\n// 6个字段的结构体.\nstruct {\n\tx, y int\n\tu float32\n\t_ float32  // padding\n\tA *[]int\n\tF func()\n}\n\n```\n\n一个指定了类型而没有指定名称的字段叫做嵌入字段，嵌入字段必须指定类型名 T 或指向非接口类型的指针类型 \\*T，其中 T 不能为指针类型。或者一个非接口类型的指针。并且T本身不能为指针类型。这种情况下会把类型名作为字段的名字。\n\n```go\n// 一个包含 4 个嵌入字段 T1, *T2, P.T3 和 *P.T4 的结构体\nstruct {\n\tT1        // 字段名为 T1\n\t*T2       // 字段名为 T2\n\tP.T3      // 字段名为 T3\n\t*P.T4     // 字段名为 T4\n\tx, y int  // 字段名为 x 和 y\n}\n```\n\n以下声明是错误的因为字段名称必须唯一。\n```go\nstruct {\n\tT     // 嵌入字段 *T 与 *P.T 冲突\n\t*T    // 嵌入字段 T 与 *P.T 冲突\n\t*P.T  // 嵌入字段 T 与 *T 冲突\n}\n```\n\n 如果 `x.f` 是表示该字段或方法 `f` 的合法选择器，则会调用结构 `x` 中嵌入字段的字段或方法 `f`。\n\n从嵌入字段组合来的字段与结构体原来的字段行为基本相同，只是不能在结构体的复合字面值中直接使用。\n\n\n给定一个结构体 S 和一个类型 T，依据以下规则生成组合后的方法集：\n\n* 如果 S 包含嵌入字段 T，则 S 和 \\*S 的方法集包括接收者为 T 的方法集，而 \\*S 包括 接收者为 \\*T 的方法集。\n* 如果 S 包含字段 \\*T。那么S和*S均包含接收者为 T 和 \\*T 的所有方法集。\n\n\n声明字段时可以给该字段添加一个字符串的 tag。这个 tag 将会成为它所对应字段的一个属性。空 tag 和缺省 tag 是相同的。tag 的值可以通过反射的接口获取，可以作为类型结构体的类型定义的一部分，也可以忽略。\n\n```\nstruct {\n\tx, y float64 \"\"  // 空 tag 和缺省 tag 相同\n\tname string  \"any string is permitted as a tag\"\n\t_    [4]byte \"ceci n'est pas un champ de structure\"\n}\n\n// 结构体对应一个 TimeStamp 的 protocol buffer.\n// tag 字符串中定义了 protocol buffer 字段对应的数字;\n// 一般使用 reflect 包读取他们.\nstruct {\n\tmicrosec  uint64 `protobuf:\"1\"`\n\tserverIP6 uint64 `protobuf:\"2\"`\n}\n```\n\n#### 指针类型\n\n指针类型表示所有指向给定类型变量的指针集合。这个指定的类型叫做指针的基础类型。没有初始化的指针值为nil。\n\n```\nPointerType = \"*\" BaseType .\nBaseType    = Type .\n```\n\n```\n*Point\n*[4]int\n```\n\n#### 函数类型\n\n函数类型可以表示所有具有相同参数类型和返回值类型的函数。未初始化的函数类型值为 nil。\n\n```\nFunctionType   = \"func\" Signature .\nSignature      = Parameters [ Result ] .\nResult         = Parameters | Type .\nParameters     = \"(\" [ ParameterList [ \",\" ] ] \")\" .\nParameterList  = ParameterDecl { \",\" ParameterDecl } .\nParameterDecl  = [ IdentifierList ] [ \"...\" ] Type .\n```\n\n在参数和返回值列表中，标识符列表必须同时存在或缺省。如果存在，那么每个名字都表示指定类型的一个参数/返回值，这些标识符必须非空并且不能重复。如果缺省，指定类型的参数/返回值使用对应的类型表示。参数列表和返回值列表一般都是需要加括号，不过在只有一个缺省返回值时，它可以不使用括号。\n\n函数的最后一个参数可以添加前缀 `...`。包含这种参数的函数叫做变参函数，它可以接收零个或多个参数。\n\n```go\nfunc()\nfunc(x int) int\nfunc(a, _ int, z float32) bool\nfunc(a, b int, z float32) (bool)\nfunc(prefix string, values ...int)\nfunc(a, b int, z float64, opt ...interface{}) (success bool)\nfunc(int, int, float64) (float64, *[]int)\nfunc(n int) func(p *T)\n```\n\n#### 接口类型\n\n接口类型指定了一个方法集。一个接口类型变量可以保存任何方法集是该接口超集的类型。我们可以认为类型实现了接口。没有初始化的接口类型值为 nil。\n\n```\nInterfaceType      = \"interface\" \"{\" { MethodSpec \";\" } \"}\" .\nMethodSpec         = MethodName Signature | InterfaceTypeName .\nMethodName         = identifier .\nInterfaceTypeName  = TypeName .\n```\n\n在接口类型的方法集中，每个方法的名称必须是非空且唯一。\n\n```\n// A simple File interface\ninterface {\n\tRead(b Buffer) bool\n\tWrite(b Buffer) bool\n\tClose()\n}\n```\n\n接口可以由多个类型实现，例如：类型 `S1` 和类型 `S2` 都有以下方法集：\n\n```\nfunc (p T) Read(b Buffer) bool { return … }\nfunc (p T) Write(b Buffer) bool { return … }\nfunc (p T) Close() { … }\n```\n\n（这里的类型 T 可以表示 `S1` 也可以表示 `S2` ） `S1` 和 `S2` 都实现了接口 `File`，而不用管类型是否还有其他方法。\n\n一个类型实现了任何方法集的为其子集的接口。因此它可能实现了多个不同接口。例如：所有的类型都实现了空接口：\n\n```go\ninterface{}\n```\n\n与之相似，思考下面这个定义为 `Locker` 的接口：\n```go\ntype Locker interface {\n\tLock()\n\tUnlock()\n}\n```\n\n如果 `S1` 和 `S2` 也实现了它：\n```go\nfunc (p T) Lock() { … }\nfunc (p T) Unlock() { … }\n```\n\n那它们就实现了两个接口 `Locker` 和 `File`。\n\n一个接口 T 可以使用另一个接口 E 来指定方法。这种方式叫做将接口 E 嵌入进接口 T。它把 E 中所有的方法（包括导出和未导出的方法）全部添加进接口 T。\n\n```go\ntype ReadWriter interface {\n\tRead(b Buffer) bool\n\tWrite(b Buffer) bool\n}\n\ntype File interface {\n\tReadWriter  // 与添加 ReadWriter 接口中的方法是等价的\n\tLocker      // 与添加 Locker 接口中的方法是等价的 \n\tClose()\n}\n\ntype LockedFile interface {\n\tLocker\n\tFile        // 无效: Lock, Unlock 不是唯一的\n\tLock()      // 无效: Lock 不是唯一的\n}\n```\n\n接口 T 不能递归的嵌入进自己或已经嵌入过它的接口。\n\n```go\n// 无效: Bad 不能嵌入它自己\ntype Bad interface {\n\tBad\n}\n\n// 无效: Bad1 不能嵌入已经引用它的 Bad2\ntype Bad1 interface {\n\tBad2\n}\ntype Bad2 interface {\n\tBad1\n}\n```\n\n#### Map类型\n\nmap 类型是一种以唯一值作为键的无序集合。\n\n```\nMapType     = \"map\" \"[\" KeyType \"]\" ElementType .\nKeyType     = Type .\n```\n\nmap的键类型必须能使用比较运算符 `==` 和 `!=` 进行比较。因此它的键类型不能是函数，map，或者切片。如果键是接口类型，那么比较运算符必须能比较他的动态值。如果不能会抛出一个运行时错误。\n\n```go\nmap[string]int\nmap[*T]struct{ x, y float64 }\nmap[string]interface{}\n```\n\nmap中元素的个数叫做它的长度。对于一个map `m`。它的长度可以通过内置函数 `len` 获得，而且它的长度可能再运行时发生变化。map 可以再运行时添加和取回元素，页可以使用内置函数 `delete `移除元素。\n\n可以使用内置函数 `make` 初始化一个新的且为空的 map。它能指定 map 的类型和预留的空间：\n\n```\nmake(map[string]int)\nmake(map[string]int, 100)\n```\n\nmap 的预留空间不会固定住 map 的长度；它可以通过添加一定数量的元素来增加自己的长度（nil map 不能添加元素）。nil map 和空 map 是相等的，只是 nil map 不能添加元素。\n\n#### Channel类型\n\nchannel提供一种手段在并发执行的函数间发送和接收指定类型的值。没有初始化的 channel 是nil。\n\n```\nChannelType = ( \"chan\" | \"chan\" \"\u003c-\" | \"\u003c-\" \"chan\" ) ElementType .\n```\n\n操作符 `\u003c-` 可以指定 channel 的数据流动方向。如果没有指定方向，channel 默认是双向的。channel 可以通过转换和赋值来限制只读和只写。\n\n```go\nchan T          // 可以接收和发送 T 类型的数据\nchan\u003c- float64  // 只能发送 float64 类型的值\n\u003c-chan int      // 只能接收\n```\n\n`\u003c-` 与最左侧的 `chan` 关联：\n\n```go\nchan\u003c- chan int    // 等价于 chan\u003c- (chan int)\nchan\u003c- \u003c-chan int  // 等价于 chan\u003c- (\u003c-chan int)\n\u003c-chan \u003c-chan int  // 等价于 \u003c-chan (\u003c-chan int)\nchan (\u003c-chan int)\n```\n\n可以通过内置的 `make` 函数初始化 channel。`make` 函数可以指定channel的类型和容量。\n\n```go\nmake(chan int, 100)\n```\n\n容量是设置了最大能缓存元素的数量。如果没有设置容量或值为 0，channel 就是没有缓存的，这时只有当发送者和接收者都准备好后才会传输数据。而带缓存的 channel 在缓存没有满的时候依然可以成功发送数据，当缓存不为空的时候可以成功接收到数据，值为 nil 的 channel 不能传输数据。\n\n可以通过内置函数 `close` 关闭 channel。在接收端的第二个返回值可以用来提示接收者在关闭的 channel 是否还包含数据。\n\nchannel 可以在发送语句，接收操作中使用。可以不考虑同步性直接在多个 goroutine 中对 channel 调用内置函数 `len` 和 `cap` 。channel 的行为和 FIFO 队列相同。举个例子，一个 goruntine 发送数据，另一个 goruntine 接收他们，接收数据的顺序和发送数据的顺序是相同的。\n\n## 类型的属性和值\n\n#### 类型标识\n\n两个类型可能相同也可能不同。\n\n定义的类型都是不同类型。如果两个类型的底层类型在结构上是相同的，那它们也是相等的。总的来说：\n* 2 个数组的长度和元素类型相同，那么它们就是相同类型。\n* 如果两个切片的元素类型相同那么它们就是相同类型。\n* 如果两个结构体字段顺序相同，并且字段名称、字段类型和 tag 都相同那么它们就是相等的。非导出字段的字段名在不同的包中总是不同的。\n* 如果两个指针的基础类型相同那么他们具有相同类型。\n* 如果两个函数具有相同的参数和返回值列表，并且他们的类型相同那么他们就是相同的，参数的名称不一定要相同。\n* 如果两个接口的方法集完全相同（方法的顺序）。\n* 如果两个 map 类型的键类型和值类型相同那它们就是相等的。 \n* 如果两个 channel 类型包含的对象类型和 channel 的方向都是相同的那它们就是相同的。\n\n给出下列声明：\n```go\ntype (\n\tA0 = []string\n\tA1 = A0\n\tA2 = struct{ a, b int }\n\tA3 = int\n\tA4 = func(A3, float64) *A0\n\tA5 = func(x int, _ float64) *[]string\n)\n\ntype (\n\tB0 A0\n\tB1 []string\n\tB2 struct{ a, b int }\n\tB3 struct{ a, c int }\n\tB4 func(int, float64) *B0\n\tB5 func(x int, y float64) *A1\n)\n\ntype\tC0 = B0\n```\n\n这些类型是相等的：\n\n```go\nA0, A1, and []string\nA2 and struct{ a, b int }\nA3 and int\nA4, func(int, float64) *[]string, and A5\n\nB0, B0, and C0\n[]int and []int\nstruct{ a, b *T5 } and struct{ a, b *T5 }\nfunc(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5\n```\n\n\nB0 和 B1 不是一种类型因为它们是通过类型定义方式分别定义的；`func(int, float64) *B0` 和 `func(x int, y float64) *[]string` 是不同的，因为 B0 和 []string 不是相同类型。\n\n#### 可分配性\n\n在以下情况下，可以将 x 分配给类型为 T 的变量（把 x 分配给 T）：\n\n* x 的类型为 T\n* x 的类型 V 和 T 有相同的底层类型并且类型 T 或 V 至少一个定义的类型\n* T 是一个接口类型并且 x 实现了 T\n* x 是一个 channel，并且 T 是channel类型，类型V和类型T有相同的元素类型，并且 2 种类型至少有一种不是定义的类型\n* x 等于 nil 并且 T 是一个指针，函数，切片，map，channel 或接口类型\n* x 是一个可以表示 T 类型值的无类型常量\n\n#### 代表性\n\n满足以下条件时可以用 T 类型的值表示常量 x：\n\n- T 值的集合包括 x\n- T 是浮点型，而 x 在没有溢出的情况下能够近似成 T 类型。近似规则使用 `IEEE 754 round-to-even`，负零和无符号的零相同。需要注意的是，常量的值不会为负零，NaN，或无限值。\n- T 为复数类型，并且 x 的 `real(x)` 和 `imag(x)` 部分由复数类型对应的浮点类型（`float32` 或 `float64` ）组成。\n\n```\nx                   T           x 可以表示 T 的值，因为：\n\n'a'                 byte        97 在 byte 类型值的集合中\n97                  rune        rune 是 int32 的别名，97 在 32 位整型值的集合中\n\"foo\"               string      \"foo\" 在字符串值的集合中\n1024                int16       1024 在 16 位整型值的集合中\n42.0                byte        42 在 8 位无符号整型值的集合中\n1e10                uint64      10000000000 在 64 位无符号整型值的集合中\n2.718281828459045   float32     2.718281828459045 的近似值 2.7182817 在 float32 类型值的集合中\n-1e-1000            float64     -1e-1000 的近视值 IEEE -0.0，等于 0 \n0i                  int         0 是整型值\n(42 + 0i)           float32     42.0 (0 虚部) 在 float32 类型值的集合中\n```\n\n```\nx                   T           x 不能表示 T 的值，因为：\n\n0                   bool        0 不在布尔值的集合中\n'a'                 string      'a' 是 rune 类型, 它不在字符串类型的值集合中\n1024                byte        1024 不在 8 位无符号整型值的集合中\n-1                  uint16      -1 不在 16 位无符号整型值的集合中\n1.1                 int         1.1 不是整型值\n42i                 float32     (0 + 42i) 不在 float32 类型值的集合中\n1e1000              float64     1e1000 取近似值时会溢出成 IEEE \n```\n\n## 代码块\n\n代码块是用大括号括起来的声明和语句。\n```\nBlock = \"{\" StatementList \"}\" .\nStatementList = { Statement \";\" } .\n```\n\n除了源码中显式的代码块，也有一些隐式的代码块。\n\n* 包含所有的Go代码的全局代码块。\n* 包含所有包的代码的包代码块。\n* 包含文件内的所有代码的文件代码块。\n* 每个 if，switch和 for 的范围都会形成隐式的块。\n* 每个 switch 和 select 条件都有自己的代码块。\n\n代码块可以嵌套并且影响作用域。\n\n## 声明和作用域\n\n一段声明可以给常量，类型，变量，函数，标签，和包绑定标识符。程序中每个标识符都需要声明。相同标识符不能在同一个代码块中声明2次。并且相同标识符不能同时在文件和 package 代码块中声明。\n\n空标识符可以和其他标识符一样在声明中使用。不过它不绑定标识符，等于没有声明。在 package 代码块中 `init` 标识符只能用做 `init` 函数的标识符，就像空标识符一样，它不会引入新的绑定。\n\n```\nDeclaration   = ConstDecl | TypeDecl | VarDecl .\nTopLevelDecl  = Declaration | FunctionDecl | MethodDecl .\n```\n\n声明过的标识符的作用域就是声明标识符所在的作用域。\n\ngo使用块来规定词汇的方位：\n* 预定义的标识符具有全局作用域。\n* 所有定义的顶级标识符具有包作用域。\n* import进来的包的名字标识符具有文件作用域。\n* 方法的接收者，函数参数，返回值变量具有函数作用域。\n* 函数内定义的参量和变量标识符的作用域是标识符被声明到容纳他的块结束。\n\n一个代码块中声明的标识符可以在它内部的代码块中重新声明。在内部代码块的作用域中标识符表示在内部代码块中声明的实体。\n\npakcage 语句不属于声明。包名不会出现在任何的作用域中。它的作用只是用来标识属于相同包的多个文件并在导入时指定默认包名。\n\n#### 标签的作用域\n\n\n可以使用标签语句来声明标签，并且可以在 `break`，`continue`，`goto` 语法中使用。如果只声明但没有使用标签时非法的。标签的作用域只有定义时的函数体，早递归函数体中没有作用。\n\n#### 空标识符\n\n空标识符使用下划线 `_` 代表。与一般的非空标识符不同，它作为匿名标识符在声明，运算元和赋值语句中都有特殊含义。\n\n#### 预定义的标识符\n\n以下标识符已经在全局作用域中预先声明：\n\n```\nTypes:\n\tbool byte complex64 complex128 error float32 float64\n\tint int8 int16 int32 int64 rune string\n\tuint uint8 uint16 uint32 uint64 uintptr\n\nConstants:\n\ttrue false iota\n\nZero value:\n\tnil\n\nFunctions:\n\tappend cap close complex copy delete imag len\n\tmake new panic print println real recover\n```\n\n####  导出标识符\n\n标识符可以导出供其他包使用。在以下两种情况同时满足时标识符是导出的：\n* 标识符的首字母是大写（Unicode 的 `Lu` 类）\n* 标识符声明在包作用域或者它是字段名/方法名。\n\n其他任何标识符都不是导出的。\n\n#### 标识符的唯一性\n\n给定一个标识符集合，一个标识符与集合中的每个标识符都不相同，那就认为这个标识符是唯一的。假设有两个标识符，如果它们的拼写不同，或者它们在不同的包中并没有导出，那它们就是不同标识符。相反，其他情况下都认为标识符是相同的。\n\n#### 常量声明\n\n常量声明使用常量表达式绑定一系列标识符。标识符的数量必须等于表达式的数量。左侧第 n 个标识符绑定右侧第 n 个表达式的值。\n\n```\nConstDecl      = \"const\" ( ConstSpec | \"(\" { ConstSpec \";\" } \")\" ) .\nConstSpec      = IdentifierList [ [ Type ] \"=\" ExpressionList ] .\n\nIdentifierList = identifier { \",\" identifier } .\nExpressionList = Expression { \",\" Expression } .\n```\n\n\n如果给定类型，常量会指定类型，并且表达式的值必须能对这个类型进行赋值。\n如果没有给定类型。常量会转换成相应的表达式类型。如果表达式的值是无类型常量，那么声明的常量也是无类型的，并且常量的标识符代表常量的值。例如：即使小数部分是 0，只要表达式是浮点数字面值，常量标识符也表示为浮点数常量。\n\n```go\nconst Pi float64 = 3.14159265358979323846\nconst zero = 0.0         // 无类型浮点数常量\nconst (\n\tsize int64 = 1024\n\teof        = -1  // 无类型整型常量\n)\nconst a, b, c = 3, 4, \"foo\"  // a = 3, b = 4, c = \"foo\", 无类型整型和字符串常量\nconst u, v float32 = 0, 3    // u = 0.0, v = 3.0\n```\n\n括号内的常量声明列表的表达式除了第一个必须声明其他表达式可以不写。空的表达式列表的值和类型都和前面的非空表达式相同。缺省的表达式列表等价于重复之前的表达式。标识符的数量必须等于表达式的数量。`iota`常量生成器是一个可以快速生成序列值的机制。\n\n```go\nconst (\n\tSunday = iota\n\tMonday\n\tTuesday\n\tWednesday\n\tThursday\n\tFriday\n\tPartyday\n\tnumberOfDays  // 非导出常量\n)\n```\n\n#### Iota\n\n在常量声明中，预定义的标识符 `iota` 表示连续的无类型整型常量。它的值为常量声明中每个常量定义的位置（从零开始）。它能够用来生成一个关联常量集合：\n\n```\nconst ( // iota is reset to 0\n\tc0 = iota  // c0 == 0\n\tc1 = iota  // c1 == 1\n\tc2 = iota  // c2 == 2\n)\n\nconst ( // iota is reset to 0\n\ta = 1 \u003c\u003c iota  // a == 1\n\tb = 1 \u003c\u003c iota  // b == 2\n\tc = 3          // c == 3  (没有使用 iota 不过它的值依然递增)\n\td = 1 \u003c\u003c iota  // d == 8\n)\n\nconst ( // iota is reset to 0\n\tu         = iota * 42  // u == 0     (无类型整型常量)\n\tv float64 = iota * 42  // v == 42.0  (float64 类型常量)\n\tw         = iota * 42  // w == 84    (无类型整型常量)\n)\n\nconst x = iota  // x == 0  (iota 被重置)\nconst y = iota  // y == 0  (iota 被重置)\n```\n\n根据定义，在同一个常量定义中多次使用 `iota` 会得到相同的值：\n\n```\nconst (\n\tbit0, mask0 = 1 \u003c\u003c iota, 1\u003c\u003ciota - 1  // bit0 == 1, mask0 == 0  (iota == 0)\n\tbit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)\n\t_, _                                  //                        (iota == 2, unused)\n\tbit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)\n)\n```\n\n最后一个例子利用了最后一个非空表达式列表的隐式重复。\n\n#### 类型声明\n\n类型声明为类型绑定一个标识符。类型声明有2种方式：类型声明和别名声明。\n\n```\nTypeDecl = \"type\" ( TypeSpec | \"(\" { TypeSpec \";\" } \")\" ) .\nTypeSpec = AliasDecl | TypeDef .\n```\n\n##### Alias声明\n\n别名声明给指定类型绑定一个标识符名称。\n\n```\nAliasDecl = identifier \"=\" Type .\n```\n\n在标识符作用域内，它作为类型的别名。\n\n```go\ntype (\n\tnodeList = []*Node  // nodeList 和 []*Node 是相同类型\n\tPolar    = polar    // Polar 和 polar 表示相同类型\n)\n```\n\n##### Type 定义\n\n类型定义会创建一个新类型并绑定一个标识符，新类型与给定类型具有相同的底层类型和操作。\n\n```\nTypeDef = identifier Type .\n```\n\n这个类型叫做定义类型，它和其他所有类型都不相同，包括创建它的类型。\n\n```go\ntype (\n\tPoint struct{ x, y float64 }  // Point 和 struct{ x, y float64 } 是不同类型\n\tpolar Point                   // polar 和 Point 表示不同类型\n)\n\ntype TreeNode struct {\n\tleft, right *TreeNode\n\tvalue *Comparable\n}\n\ntype Block interface {\n\tBlockSize() int\n\tEncrypt(src, dst []byte)\n\tDecrypt(src, dst []byte)\n}\n```\n\n\n\n定义类型可以关联该类型的方法。它不会继承原来类型的任何方法。但是接口类型的方法集和类型的结构没有改变。\n\n```go\n// Mutex 是一个拥有 Lock 和 Unlock 两个方法的数据类型。\ntype Mutex struct         { /* Mutex fields */ }\nfunc (m *Mutex) Lock()    { /* Lock implementation */ }\nfunc (m *Mutex) Unlock()  { /* Unlock implementation */ }\n\n// NewMutex 与 Mutex 结构相同不过方法集为空。\ntype NewMutex Mutex\n\n// PtrMutex 的底层类型 *Mutex 的方法集没有改变，\n// 但是 PtrMutex 的方法集为空。\ntype PtrMutex *Mutex\n\n// *PrintableMutex 包含嵌入字段 Mutex 的 Lock 和 Unlock 方法。\ntype PrintableMutex struct {\n\tMutex\n}\n\n// MyBlock 是与 Block 有相同方法集的接口类型\ntype MyBlock Block\n```\n\n类型定义可以定义方法集不同的布尔值、数字和字符串类型：\n```go\ntype TimeZone int\n\nconst (\n\tEST TimeZone = -(5 + iota)\n\tCST\n\tMST\n\tPST\n)\n\nfunc (tz TimeZone) String() string {\n\treturn fmt.Sprintf(\"GMT%+dh\", tz)\n}\n```\n\n#### 变量声明\n\n变量声明可以创建一个或多个变量，并绑定对应的标识符、指定类型和初始值。\n\n```\nVarDecl     = \"var\" ( VarSpec | \"(\" { VarSpec \";\" } \")\" ) .\nVarSpec     = IdentifierList ( Type [ \"=\" ExpressionList ] | \"=\" ExpressionList ) .\n```\n\n```go\nvar i int\nvar U, V, W float64\nvar k = 0\nvar x, y float32 = -1, -2\nvar (\n\ti       int\n\tu, v, s = 2.0, 3.0, \"bar\"\n)\nvar re, im = complexSqrt(-1)\nvar _, found = entries[name]  // map lookup; only interested in \"found\"\n```\n\n如果给定一个表达式列表。变量会根据赋值规则使用表达式进行初始化。否则，每个变量都会初始化成变量类型的零值。\n\n如果指定类型，变量会为指定类型。如果没有指定类型，变量会使用分配的初始值类型。如果初始值为无类型常量，它会转换成初始值的默认类型。如果是一个无类型布尔值，那么变量的类型就是 `bool`。值 `nil` 不能给没有指定类型的变量赋值。\n\n```go\nvar d = math.Sin(0.5)  // d is float64\nvar i = 42             // i is int\nvar t, ok = x.(T)      // t is T, ok is bool\nvar n = nil            // illegal\n```\n\n实现的限制：在函数体内声明的变量如果没有使用过编译器需要报错。\n\n#### 短变量声明\n\n短变量声明的语法:\n\n```\nShortVarDecl = IdentifierList \":=\" ExpressionList .\n```\n\n它比正常使用初始化表达式进行变量声明的方式要短，而且不指定类型：\n\n```\n\"var\" IdentifierList = ExpressionList .\n```\n\n```go\ni, j := 0, 10\nf := func() int { return 7 }\nch := make(chan int)\nr, w := os.Pipe(fd)  // os.Pipe() 返回两个值\n_, y, _ := coord(p)  // coord() 返回三个值，我们只关注 y\n```\n\n和常规变量声明不同，即使之前在相同代码块中声明过的变量，也可以在短变量重新声明相同类型的变量，并且保证至少会有一个新的非空变量。总之，只应该在多变量短声明的时候重新声明变量，重新声明并不会使用新的变量，而是给变量分配新值。\n\n```go\nfield1, offset := nextField(str, 0)\nfield2, offset := nextField(str, offset)  // 重新声明 offset\na, a := 1, 2                              // 非法：声明了 a 两次并且没有新的变量\n```\n\n短变量声明只能在函数中使用，例如在 `if`、`for`、`switch`语句的上下文中声明临时变量。\n\n#### 函数声明\n\n函数声明为函数绑定标识符。\n\n```\nFunctionDecl = \"func\" FunctionName Signature [ FunctionBody ] .\nFunctionName = identifier .\nFunctionBody = Block .\n```\n\n 如果函数指定了返回参数。函数体的语句必须以终止语句结束。\n\n ```go\n func IndexRune(s string, r rune) int {\n\tfor i, c := range s {\n\t\tif c == r {\n\t\t\treturn i\n\t\t}\n\t}\n\t// 无效：缺少 return 语句\n}\n ```\n\n函数声明可以没有函数体。这样的声明提供一个函数声明，并由其他外部实现，例如汇编脚本。\n\n```go\nfunc min(x int, y int) int {\n\tif x \u003c y {\n\t\treturn x\n\t}\n\treturn y\n}\n\nfunc flushICache(begin, end uintptr)  // 由外部实现\n```\n\n\n#### 方法声明\n\n方法是一个带接收者的函数，方法声明为方法绑定标识符作为方法名并指定方法对应的接收者类型。\n\n```\nMethodDecl = \"func\" Receiver MethodName Signature [ FunctionBody ] .\nReceiver   = Parameters .\n```\n\n接收者通过在方法增加一个额外的参数来指定。这个参数必须是一个非可变参数。它的类型必须是 T 或者 T 的指针（可能包含括号）。T 被称作接收者的基础类型；它不能是指针或接口类型，并且只能在同一个包中定义方法。声明后，我们认为方法绑定了基础类型，并且可以通过 T 或 *T 选择器访问方法名。\n\n非空的接收者标识符在方法签名中必须是唯一的。如果接收者的值没有在该方法中使用，那么接收者标识符可以省略。函数和方法的参数也是一样。\n\n对于一个基础类型。绑定的非空的方法名必须是唯一的。如果基础类型是一个结构体，非空的方法名也不能与结构体字段重复。\n\n给定一个`Point`类型。声明：\n```\nfunc (p *Point) Length() float64 {\n\treturn math.Sqrt(p.x * p.x + p.y * p.y)\n}\n\nfunc (p *Point) Scale(factor float64) {\n\tp.x *= factor\n\tp.y *= factor\n}\n```\n\n为类型 `*Point `绑定了2个方法 `Length` 和 `Scale`。\n方法的类型就是以接收者作为第一个参数的函数类型，例如 `Scale` 方法：\n```go\nfunc(p *Point, factor float64)\n```\n\n但是以这种方式声明的函数并不是方法。\n\n## 表达式\n\n 表达式通过针对运算元使用运算符和函数来获取计算值。\n\n#### 运算元\n\n运算元代表表达式中的一个简单的。运算元可以是字面值，非空标识符。或括号表达式。\n\n空标识符只能出现在赋值声明的左侧。\n\n```\nOperand     = Literal | OperandName | MethodExpr | \"(\" Expression \")\" .\nLiteral     = BasicLit | CompositeLit | FunctionLit .\nBasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .\nOperandName = identifier | QualifiedIdent.\n```\n\n#### 修饰标识符\n\n修饰标识符是以包名作为前缀修饰的标识符。包名和标识符都不能为空。\n\n```\nQualifiedIdent = PackageName \".\" identifier .\n```\n\n修饰标识符可以用来访问不同包（需要先导入）中的标识符。标识符必须是导出的并在包级代码块声明才能够被访问。\n\n```\nmath.Sin\t// 表示 math 包中的 Sin 函数\n```\n\n#### 复合字面值\n\n复合字面值能为结构体、数组、切片和 map 初始化值。它每次只能创建一个值。字面值由一个字面值类型和使用括号括起来的元素列表组成。元素前也可以声明元素对应的键。\n\n```\nCompositeLit  = LiteralType LiteralValue .\nLiteralType   = StructType | ArrayType | \"[\" \"...\" \"]\" ElementType |\n                SliceType | MapType | TypeName .\nLiteralValue  = \"{\" [ ElementList [ \",\" ] ] \"}\" .\nElementList   = KeyedElement { \",\" KeyedElement } .\nKeyedElement  = [ Key \":\" ] Element .\nKey           = FieldName | Expression | LiteralValue .\nFieldName     = identifier .\nElement       = Expression | LiteralValue .\n```\n\n字面值类型的底层类型必须是一个结构体，数组，切片或 map 类型（如果没有指定类型名就会强制执行这个约束）。元素的类型和键都必须能够分配给相应的字段的元素和键类型；没有额外的类型转换。键可以表示结构体的字段名，切片和数组的索引，map 类型的键。对于 map 字面值，所有的元素都必须有键。如果相同字段名或常量值的键对应多个元素就会报错。如果 map 类型的键为非常量类型，请看求值顺序章节。\n\n结构体字面值遵循以下规则：\n* 在结构体中，键必须是它的字段名。\n* 不包含任何键的元素列表的顺序需要与结构体字段的声明顺序相同。\n* 如果一个元素指定了键，那么所有的元素都必须指定键。\n* 包含键的元素列表不需要指定结构体的每个字字段，缺省字段会使用字段类型的零值。\n* 字面值可以不指定元素；这样的字面值等于该类型的零值。\n* 指定非本包的非导出字段会报错。\n\n给定声明：\n```go\ntype Point3D struct { x, y, z float64 }\ntype Line struct { p, q Point3D }\n```\n\n我们可以使用这种写法：\n\n```go\norigin := Point3D{}                            // Point3D 的零值\nline := Line{origin, Point3D{y: -4, z: 12.3}}  // line.q.x 的零值\n```\n\n数组和切片遵循以下规则：\n* 每个元素都关联一个数字索引标记元素再数组中的位置。\n* 给元素指定的键会作为它的索引。键必须是能够表示非负的 `int` 类型值的常量；如果是指定类型的常量，那么常量必须是整型。\n* 元素没有指定键时会使用之前的索引加一。如果第一个元素没有指定键，它的索引为零。\n\n对复合字面值取址会生成指向由字面量初始化的变量的指针。\n\n```go\nvar pointer *Point3D = \u0026Point3D{y: 1000}\n```\n\n数组字面值需要在类型中指定数组的长度。如果提供的元素少于数组的长度，那么缺少元素的位置将会使用元素类型的零值替代。如果索引超过数组的长度会报错。`…` 表示数组的长度等于最大元素索引加一。\n\n```go\nbuffer := [10]string{}             // len(buffer) == 10\nintSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6\ndays := [...]string{\"Sat\", \"Sun\"}  // len(days) == 2\n```\n\n切片字面值底层其实就是数组字面值。因此它的长度和容量都是元素的最大索引加一。切片字面值的格式为：\n```go\n[]T{x1, x2, … xn}\n```\n可以在数组上进行切片操作从而获得切片：\n```go\ntmp := [n]T{x1, x2, … xn}\ntmp[0 : n]\n```\n\n在一个数组、切片或 map 类型 T 中。元素或者 map 的键可能有自己的字面值类型，如果字面值类型和元素或者键类型相同，那么对应的类型标识符可以省略。与之类似，如果元素或键的类型为 `*T`，那么它们的 `\u0026T` 也可以省略。\n\n```go\n[...]Point{{1.5, -3.5}, {0, 0}}     // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}\n[][]int{{1, 2, 3}, {4, 5}}          // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}\n[][]Point{{{0, 1}, {1, 2}}}         // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}\nmap[string]Point{\"orig\": {0, 0}}    // same as map[string]Point{\"orig\": Point{0, 0}}\nmap[Point]string{{0, 0}: \"orig\"}    // same as map[Point]string{Point{0, 0}: \"orig\"}\n\ntype PPoint *Point\n[2]*Point{{1.5, -3.5}, {}}          // same as [2]*Point{\u0026Point{1.5, -3.5}, \u0026Point{}}\n[2]PPoint{{1.5, -3.5}, {}}          // same as [2]PPoint{PPoint(\u0026Point{1.5, -3.5}), PPoint(\u0026Point{})}\n```\n\n当复合字面值使用字面值类型的类型名格式出现在 `if`、`for` 或 `switch` 语句的关键字和括号之间并且没有使用圆括号包裹的时候，会引发语法歧义。在这种特殊的情况下字面值的括号会被认为是语句的代码块。为了避免歧义，复合字面值必须用括号括起来。\n\n```go\nif x == (T{a,b,c}[i]) { … }\nif (x == T{a,b,c}[i]) { … }\n```\n\n下面是合法的数组、切片和 map 的例子：\n```go\n// list of prime numbers\nprimes := []int{2, 3, 5, 7, 9, 2147483647}\n\n// vowels[ch] is true if ch is a vowel\nvowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}\n\n// the array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}\nfilter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}\n\n// frequencies in Hz for equal-tempered scale (A4 = 440Hz)\nnoteFrequency := map[string]float32{\n\t\"C0\": 16.35, \"D0\": 18.35, \"E0\": 20.60, \"F0\": 21.83,\n\t\"G0\": 24.50, \"A0\": 27.50, \"B0\": 30.87,\n}\n```\n\n#### 函数字面值\n\n函数字面值表示一个匿名函数。\n\n```\nFunctionLit = \"func\" Function .\n```\n\n```go\nfunc(a, b int, z float64) bool { return a*b \u003c int(z) }\n```\n\n函数字面值能分配给变量或直接调用。\n\n函数字面值是一个闭包。它可以引用包裹函数中的变量，这些变量在包裹函数和函数字面值之间是共享的。并且它会一直存在直到生命周期结束。\n\n#### 主要表达式\n\n主要表达式是一元和二元表达式的运算元。\n\n```\nPrimaryExpr =\n\tOperand |\n\tConversion |\n\tPrimaryExpr Selector |\n\tPrimaryExpr Index |\n\tPrimaryExpr Slice |\n\tPrimaryExpr TypeAssertion |\n\tPrimaryExpr Arguments .\n\nSelector       = \".\" identifier .\nIndex          = \"[\" Expression \"]\" .\nSlice          = \"[\" [ Expression ] \":\" [ Expression ] \"]\" |\n                 \"[\" [ Expression ] \":\" Expression \":\" Expression \"]\" .\nTypeAssertion  = \".\" \"(\" Type \")\" .\nArguments      = \"(\" [ ( ExpressionList | Type [ \",\" ExpressionList ] ) [ \"...\" ] [ \",\" ] ] \")\" .\n```\n\n```go\nx\n2\n(s + \".txt\")\nf(3.1415, true)\nPoint{1, 2}\nm[\"foo\"]\ns[i : j + 1]\nobj.color\nf.p[i].x()\n```\n\n#### 选择器\n\n对于一个 x 不是包名的主要表达式，选择器表达式：\n```\nx.f\n```\n\n表示 x 的字段或方法 f（有时为 *x）。标识符 f 叫做（字段/方法）选择器。它不能是空标识符。选择器表达式的类型就是 f 的类型。如果 x 是包名。请参考修饰标识符。\n\n选择器 f 可以表示类型 T 的方法或字段 f。也可以表示类型 T 的嵌入方法或字段 f。访问 f 所需穿过的嵌套层数叫做它在类型 T 中的深度。声明在 T 中的字段或方法的深度为 0。声明在 T 的嵌入字段 A 中的方法或字段的深度等于 f 在 A 中的深度加一。\n\n选择器遵循以下原则：\n\n* 对于非指针/接口类型 T/*T 的值 x，x.f 表示第一层的方法/字段。如果在第一层没有对应的 f，选择器表达式就是非法的。\n* 对于接口类型 I 的值 x，`x.f`表示动态值 x 的方法名 f。如果接口 I 的方法集中没有 f 方法，选择器就是非法的。\n* 作为例外，如果 x 是一个指针类型并且 (\\*x).f 是合法的选择器表达式（只能表示字段，不能表示方法）。那么(*x).f 可以简写成 x.f。\n* 在其他情况下，x.f 都是非法的。\n* 如果x是指针类型，并且值为 nil，其中 f 为结构体字段。赋值或取值 x.f 会引起运行时恐慌。\n* 如果x是接口类型，并且值为 nil。调用 x.f  会引起运行时恐慌。\n\n例如给定声明：\n\n```go\ntype T0 struct {\n\tx int\n}\n\nfunc (*T0) M0()\n\ntype T1 struct {\n\ty int\n}\n\nfunc (T1) M1()\n\ntype T2 struct {\n\tz int\n\tT1\n\t*T0\n}\n\nfunc (*T2) M2()\n\ntype Q *T2\n\nvar t T2     // with t.T0 != nil\nvar p *T2    // with p != nil and (*p).T0 != nil\nvar q Q = p\n```\n\n结果：\n\n```go\nt.z          // t.z\nt.y          // t.T1.y\nt.x          // (*t.T0).x\n\np.z          // (*p).z\np.y          // (*p).T1.y\np.x          // (*(*p).T0).x\n\nq.x          // (*(*q).T0).x        (*q).x is a valid field selector\n\np.M0()       // ((*p).T0).M0()      M0 expects *T0 receiver\np.M1()       // ((*p).T1).M1()      M1 expects T1 receiver\np.M2()       // p.M2()              M2 expects *T2 receiver\nt.M2()       // (\u0026t).M2()           M2 expects *T2 receiver, see section on Calls\n```\n\n但是下面这种方式是不合法的：\n\n```go\nq.M0()       // (*q).M0 is valid but not a field selector\n```\n\n\n#### 方法表达式\n\n如果 M 在类型 T 的方法集中。那么 T.M 就是能够正常调用的函数。使用与 M 相同的参数只是在参数列表的最前面增加了接收者参数。\n\n```go\nMethodExpr    = ReceiverType \".\" MethodName .\nReceiverType  = TypeName | \"(\" \"*\" TypeName \")\" | \"(\" ReceiverType \")\" .\n```\n\n\n假设结构体 T 有两个方法。接收者类型为 T 的 Mv 方法和接收者类型为 *T 的 Mp 方法：\n\n```go\ntype T struct {\n\ta int\n}\nfunc (tv  T) Mv(a int) int         { return 0 }  // value receiver\nfunc (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver\n\nvar t T\n```\n\n表达式\n\n```go\nT.Mv\n```\n\n将会生成一个等价于 Mv 方法只是第一个参数显式声明接受者的函数。它的签名为：\n\n```go\nfunc(tv T, a int) int\n```\n\n这个函数能够通过接收者正常调用，以下5种方式是等价的：\n\n```go\nt.Mv(7)\nT.Mv(t, 7)\n(T).Mv(t, 7)\nf1 := T.Mv; f1(t, 7)\nf2 := (T).Mv; f2(t, 7)\n```\n\n与之类似：\n\n```go\n(*T).Mp\n```\n\n生成表示 Mp 的函数签名：\n\n```go\nfunc(tp *T, f float32) float32\n```\n\n对于一个把值作为接收者的方法，我们可以显式的从指针接收者获得函数：\n\n```go\n(*T).Mv\n```\n\n生成表示 Mv 的函数签名：\n\n```go\nfunc(tv *T, a int) int\n```\n\n这样的函数会通过接收者间接的创建一个值作为接收者传入底层方法中。方法内不能修改接收者的值，因为它的地址是在函数的调用栈里面。\n\n最后一个例子。把值作为接收者函数当做指针作为接收者的方法是非法的，因为指针接收者的方法集中不包含值类型的方法集。\n\n\n通过函数调用语法从方法中获取函数的值。接收者作为调用函数的第一个参数。给定  `f :=T.Mv`，f 作为`f(t,7)` 进行调用而不是 `t.f(7)`。想创建一个绑定接收者的函数可以使用函数字面值或者方法值。\n\n在接口类型中定义函数获取函数值是合法的。最终的函数调用会使用接口类型作为接收者。\n\n####  方法值\n\n如果表达式 x 拥有静态类型 T 并且 M 在类型 T 的方法集中。x.M 叫做方法值。方法值 `x.M` 是一个函数值，这个函数和 x.M 拥有相同的参数列表。表达式 x 在计算方法值时会被保存和计算，这个拷贝的副本会作为任何接下来调用的接收者。\n\n类型 T 可能是接口类型也可能不是接口类型。\n\n\n与方法表达式中讲过的一样，假设类型 T 有两个方法：接收者类型为 T 的 Mv 和接受者类型为 *T 的 Mp ：\n\n```\ntype T struct {\n\ta int\n}\nfunc (tv  T) Mv(a int) int         { return 0 }  // value receiver\nfunc (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver\n\nvar t T\nvar pt *T\nfunc makeT() T\n```\n\n表达式：\n\n```go\nt.Mv\n```\n\n生成一个类型的函数值：\n\n```go\nfunc(int) int\n```\n\n以下两种调用是等价的：\n\n```go\nt.Mv(7)\nf := t.Mv; f(7)\n```\n\n相似的，表达式：\n\n```go\npt.Mp\n```\n\n生成一个类型的函数值：\n\n```go\nfunc(float32) float32\n```\n\n与选择器相同，使用指针调用以值作为接收者的非接口方法会自动将指针解引用：`pt.Mv` 等价于 `(*pt).Mv`。\n\n与方法调用相同，使用值调用以指针作为接收者的非接口方法会自动对值取址：`pt.Mv` 等价于 `(\u0026pt).Mv`。\n\n```go\n\nf := t.Mv; f(7)   // like t.Mv(7)\nf := pt.Mp; f(7)  // like pt.Mp(7)\nf := pt.Mv; f(7)  // like (*pt).Mv(7)\nf := t.Mp; f(7)   // like (\u0026t).Mp(7)\nf := makeT().Mp   // invalid: result of makeT() is not addressable\n```\n\n尽管上面使用的都是非接口类型的例子，不过对于接口类型同样适用。\n\n```go\nvar i interface { M(int) } = myVal\nf := i.M; f(7)  // like i.M(7)\n```\n\n\n#### index表达式\n\n主要表达式格式：\n\n```\na[x]\n```\n\n可以表示数组元素、数组的指针、切片、字符串或 map 类型 a 索引 x 对应的值。x 称作索引或者 map 的键。遵循以下规则：\n\n如果a不是 map 类型：\n\n* 索引 x 必须是整型或无类型常量。\n* 常量索引必须是非负数且可以使用 int 类型表示。\n* 无类型的常量索引会作为 int 型的值。\n* 索引 x 的范围在 `0\u003c=x\u003clen(a)` 内，否则就是越界。\n\n对于数组类型 A：\n\n * 常量索引必须在合法范围内。\n * 如果 x 在运行时越界会引起运行时恐慌。\n * `a[x]` 表示数组在索引 x 处的元素。`a[x]` 的类型就是 A 的元素类型。\n\n 对于数组的指针类型：\n\n * 可以使用 `a[x]` 表示 `(*a)[x]`。\n\n 对于切片类型 S：\n\n * 如果 x 在运行时越界会引起运行时恐慌。\n * `a[x]` 表示切片在索引 x 处的元素。`a[x]` 的类型就是 S 的元素类型。\n\n 对于字符串类型：\n\n * 如果字符串 a 为常量，那么常量索引必须在合法范围内。\n * 如果 x 在运行时越界会引起运行时恐慌。\n * a[x] 表示索引 x 处的非常量字节，它是byte类型。\n * 不能对 a[x] 分配值。\n\n 对于 map 类型 M：\n\n * 必须保证 x 的类型能够给 M 的键分配值。\n * 如果map包含键为 x 的值，a[x] 就是 map 中键 x 对应的值，它的类型就是 M 的元素类型。\n * 如果 map 值为 nil 或不包含这个实体，那么 a[x] 为 M 元素类型的零值。\n\n 否则 a[x] 就是非法的。\n\n基于 `map[K]V` 类型  a 的索引表达式可以使用特殊格式的赋值和初始化语法。\n\n ```go\nv, ok = a[x]\nv, ok := a[x]\nvar v, ok = a[x]\n ```\n\n它会额外生成一个无类型的布尔值。如果 ok 是 true，那么代表在map中有该键，如果没有 ok 为 false。\n\n给一个值为 nil 的 map 类型变量赋值会导致运行时恐慌。\n\n#### 切片表达式\n\n切片表达式可以基于字符串、数组、数组指针、切片创建字符串子串或切片。它有两种变体，一种是简单的格式是指定开始和结束位置，完全格式的语法还可以指定容量。\n\n####### 简单切片表达式\n\n对于数组、字符串、指针数组、切片 a，主要表达式：\n\n```go\na[low:high]\n```\n\n可以构造字符串子串或切片。索引 `low` 和 `high` 决定结果切片中的元素。结果切片的索引从 0 开始，长度为 `high - low`。从数组切分出的切片 s 拥有类型 `[]int`，长度为 3 ，容积为 4。\n\n```go\na := [5]int{1, 2, 3, 4, 5}\ns := a[1:4]\n```\n\n```go\ns[0] == 2\ns[1] == 3\ns[2] == 4\n```\n\n为了方便起见，索引值都可以缺省。当 low 缺省时默认从 0 开始。当缺 high 缺省时默认的取切片的长度。\n\n```go\na[2:]  // same as a[2 : len(a)]\na[:3]  // same as a[0 : 3]\na[:]   // same as a[0 : len(a)]\n```\n\n如果 a 是一个数组指针，那么 `a[low:high]` 可以表示 `(*a)[low : high]`。\n\n对于数组或者字符串，索引的范围是`0\u003c=low\u003c=high\u003c=len(a)`。对于切片，最大的索引值可以为切片的容量，而不是切片的长度。常量索引必须为非负数，且能够转换成 int 类型。对于数组或者常量字符串。常量索引值必须在合法范围内。如果2个索引都是常量。low 必须小于 high。如果索引在运行时访问了非法内存，程序会发生运行时恐慌。\n\n除了无类型字符串，对于切片和字符串的操作结果是非常量类型的值，它的类型与运算元相同。如果运算元为无类型字符串，那么结果类型会为 string。如果把数组作为运算元，它必须是可寻址的，并且获得的切片和原数组具有同一元素类型。\n\n如果切片运算元为 nil，那么结果也是 nil。否则结果切片会和运算元共享相同的底层无类型数组。\n\n###### 完全切片表达式\n\n对于数组，数组指针或非字符串切片，主要表达式为：\n\n```go\na[low : high : max]\n```\n\n它会构造一个同类型切片，并具有与简单切片表达式的 `a[low:high]` 相同的长度和元素。另外，它还可以把切片的容量设置为 `max - low`。这时只有第一个索引可以为缺省值，默认为零。从数组中获得切片以后：\n\n```go\na := [5]int{1, 2, 3, 4, 5}\nt := a[1:3:5]\n```\n\n\n 切片 t 为 []int 类型，长度为 2，容量为 4，并且元素为：\n\n ```go\nt[0] == 2\nt[1] == 3\n ```\n\n 和简单切片表达式一样，如果 a 是数组指针 ，那么 `a[low:high:max]` 可以简写为 `(*a)[low:high:max]`。如果切分操作元是数组，那么这个数组必须是可以寻址的。\n\n如果索引必须在 `0 \u003c= low \u003c= high \u003c= max \u003c= cap(a)` 范围内。常量索引不能是负数并且能够使用 `int` 类型表示；对于数组，索引必须在合法范围内。如果有多个索引都是常量的，那么所有索引都需要在合法范围内。如果索引是非法的，会引起运行时恐慌。\n\n#### 类型断言\n\n 对于接口类型 x 和类型 T，主要表达式：\n ```go\n x.(T)\n ```\n\n 可以断言 x 不是 nil  且 x 的值是 T 类型。标记 x.(T) 叫做类型断言。\n\n 更确切的说，如果 T 不是接口类型，那么 x.(T) 将会断言动态类型 x 的类型是不是 T。\n 这时，T 必须实现了 x 的（接口）类型。否则断言会是非法的因为 x 不能保存 T 类型的值。如果 T 是接口类型，那么可以断言动态类型 x 是否实现了 T 接口。\n\n\n 如果类型断言成功，表达式的值为 x 的值，但它的类型是T。如果断言失败，将会导致运行时恐慌。换句话说，即使 x 是运行时确定的，x.(T) 也必须是编程时就确认存在的。\n\n ```go\n var x interface{} = 7          // x 拥有动态类型 int 值为 7\ni := x.(int)                   // i 为 int 类型值为 7\n\ntype I interface { m() }\n\nfunc f(y I) {\n\ts := y.(string)        // 非法: 字符串没有实现接口 I （缺少 m 方法）\n\tr := y.(io.Reader)     // r 拥有接口 io.Reader 所以 y 的动态类型必须同时实现 I 和 io.Reader\n\t…\n}\n ```\n\n 类型断言可以使用特定格式的赋值和初始化语句。\n\n ```go\n v, ok = x.(T)\nv, ok := x.(T)\nvar v, ok = x.(T)\nvar v, ok T1 = x.(T)\n ```\n\n 这时将会额外生成一个无类型的布尔值。如果断言成功，ok返回 `true`，否则是 `false`。并且 v 会是 T 类型的零值。这时不会有恐慌发生。\n\n#### 调用\n\n给定函数类型为 F 的表达式 f：\n\n```go\nf(a1, a2, … an)\n```\n\n可以使用 a1,a2...an 来调用函数 f。除一种特殊情况之外，函数参数必须是对应 F 函数参数类型的单值表达式，且在函数调用前就已经完成求值。表达式的结果类型是 f 的结果类型。函数调用和方法调用相似，只是方法额外需要一个接收者类型。\n\n```go\nmath.Atan2(x, y)  // function call\nvar pt *Point\npt.Scale(3.5)     // method call with receiver pt\n```\n\n在函数调用中，函数的值和参数是按照顺序求值的。在计算之后作为参数会传进函数，函数开始执行。当函数执行完成后返回的参数将会返回给函数的调用者。\n\n调用值为 nil 的函数会导致运行时恐慌。\n\n作为特例，如果函数或者方法的返回值等于参数列表的个数，那么会嵌套调用。这将把返回值直接赋值给下一次调用函数的参数。\n\n```go\nfunc Split(s string, pos int) (string, string) {\n\treturn s[0:pos], s[pos:]\n}\n\nfunc Join(s, t string) string {\n\treturn s + t\n}\n\nif Join(Split(value, len(value)/2)) != value {\n\tlog.Panic(\"test fails\")\n}\n```\n\n如果 x 的方法集中包含 m 那么 `x.m()` 是合法的。并且参数列表和 m 的参数列表相同。如果x是可寻址的，那么那么x指针的方法集`(\u0026x).m()`可以简写成`x.m()`。\n\n```go\nvar p Point\np.Scale(3.5)\n```\n\n没有方法类型，也没有方法字面值。\n\n#### 通过 `...` 来传递参数\n\n如果 f 的最后一个参数 p 的类型是 `...T`。那么在函数内部 p 参数的类型就是 `[]T`。如果 f 调用时没有传入 p 对应的参数，那么p为 nil。否则这些参数会以切片方式传入，在新的底层切片中。切片中的类型都是能赋值给类型 T 的值。这个切片的长度和容量在不同的调用中有所不同。\n\n给定函数调用：\n\n```go\nfunc Greeting(prefix string, who ...string)\nGreeting(\"nobody\")\nGreeting(\"hello:\", \"Joe\", \"Anna\", \"Eileen\")\n```\n\n在 `Greeting` 中，第一次调用时，`who`是 nil 类型。而在第二次调用时是`[]string{\"Joe\", \"Anna\", \"Eileen\"}`。\n\n如果在调用的时候的最后一个参数是[]T，那么我们可以使用`...`来将切片中的值依次赋值给参数列表。\n\n给定切片s并且调用:\n\n```go\ns := []string{\"James\", \"Jasmine\"}\nGreeting(\"goodbye:\", s...)\n```\n\nz 在 `Greeting`。中 `who` 会和切片 s 共享相同的底层数组。\n\n#### 操作符\n\n操作符用来连接运算元。\n\n```\nExpression = UnaryExpr | Expression binary_op Expression .\nUnaryExpr  = PrimaryExpr | unary_op UnaryExpr .\n\nbinary_op  = \"||\" | \"\u0026\u0026\" | rel_op | add_op | mul_op .\nrel_op     = \"==\" | \"!=\" | \"\u003c\" | \"\u003c=\" | \"\u003e\" | \"\u003e=\" .\nadd_op     = \"+\" | \"-\" | \"|\" | \"^\" .\nmul_op     = \"*\" | \"/\" | \"%\" | \"\u003c\u003c\" | \"\u003e\u003e\" | \"\u0026\" | \"\u0026^\" .\n\nunary_op   = \"+\" | \"-\" | \"!\" | \"^\" | \"*\" | \"\u0026\" | \"\u003c-\" .\n```\n\n\n比较运算符在此处讨论。对于其他二元操作符，两个操作元的类型必须是相同的，除了位移和无类型常量。针对常量的操作，请看常量表达式章节。\n\n除了位移操作，如果其中一个操作符是无类型常量，而另个不是，那么无类型的常量会转换成另一个运算元的类型。\n\n在右移表达式中的运算元必须是无符号的整数或者可以转换成 uint 的无类型的常量。如果左移一个无类型常量那么结果依然是无类型的。他首先会转换成指定类型。\n\n```go\nvar s uint = 33\nvar i = 1\u003c\u003cs           // 1 has type int\nvar j int32 = 1\u003c\u003cs     // 1 has type int32; j == 0\nvar k = uint64(1\u003c\u003cs)   // 1 has type uint64; k == 1\u003c\u003c33\nvar m int = 1.0\u003c\u003cs     // 1.0 has type int; m == 0 if ints are 32bits in size\nvar n = 1.0\u003c\u003cs == j    // 1.0 has type int32; n == true\nvar o = 1\u003c\u003cs == 2\u003c\u003cs   // 1 and 2 have type int; o == true if ints are 32bits in size\nvar p = 1\u003c\u003cs == 1\u003c\u003c33  // illegal if ints are 32bits in size: 1 has type int, but 1\u003c\u003c33 overflows int\nvar u = 1.0\u003c\u003cs         // illegal: 1.0 has type float64, cannot shift\nvar u1 = 1.0\u003c\u003cs != 0   // illegal: 1.0 has type float64, cannot shift\nvar u2 = 1\u003c\u003cs != 1.0   // illegal: 1 has type float64, cannot shift\nvar v float32 = 1\u003c\u003cs   // illegal: 1 has type float32, cannot shift\nvar w int64 = 1.0\u003c\u003c33  // 1.0\u003c\u003c33 is a constant shift expression\n```\n\n\n###### 运算符优先级\n\n一元运算符拥有最高优先级。++ 和 -- 是语句而不是表达式，他们在运算符的优先级之外。所以 (\\*p)++ 和 *p++ 是一样的。\n\n二元运算符有 5 个优先级。乘法运算符在最高级，紧接着是加法运算符。比较运算符，\u0026\u0026 运算符，最后是 ||。\n\n```\nPrecedence    Operator\n    5             *  /  %  \u003c\u003c  \u003e\u003e  \u0026  \u0026^\n    4             +  -  |  ^\n    3             ==  !=  \u003c  \u003c=  \u003e  \u003e=\n    2             \u0026\u0026\n    1             ||\n```\n\n相同优先级的二元运算符的执行顺序是由左到右。例如 `x/y*z`和`(x/y)*z` 是一样的。\n\n```\n+x\n23 + 3*x[i]\nx \u003c= f()\n^a \u003e\u003e b\nf() || g()\nx == y+1 \u0026\u0026 \u003c-chanPtr \u003e 0\n```\n\n#### 算数运算符\n\n算数运算符应用在 2 个数字值之间，别切生成一个相同类型的值作为第一个运算元。四种算数运算符(+,-,*,/)应用在数字，浮点，复合类型之中。+ 也可以用于字符串。位运算和位移运算只适用于整数。\n\n```\n+    sum                    integers, floats, complex values, strings\n-    difference             integers, floats, complex values\n*    product                integers, floats, complex values\n/    quotient               integers, floats, complex values\n%    remainder              integers\n\n\u0026    bitwise AND            integers\n|    bitwise OR             integers\n^    bitwise XOR            integers\n\u0026^   bit clear (AND NOT)    integers\n\n\u003c\u003c   left shift             integer \u003c\u003c unsigned integer\n\u003e\u003e   right shift            integer \u003e\u003e unsigned integer\n```\n\n###### 数字运算符\n\n对于两个整数 x 和 y。整数商 `q=x/y` 和余数 `r=x%y` 遵循以下规律。\n\n```\nx = q*y + r  and  |r| \u003c |y|\n```\n\n`x/y` 截断为 0。\n\n```\n x     y     x / y     x % y\n 5     3       1         2\n-5     3      -1        -2\n 5    -3      -1         2\n-5    -3       1        -2\n```\n\n作为这个规则的例外情况，如果 x 非常大，那么 `q=x/-1` 等于 x。\n\n```\n\t\t\t \t\t   x, q\nint8                     -128\nint16                  -32768\nint32             -2147483648\nint64    -9223372036854775808\n```\n\n如果除数是一个常量。那么它不能是 0，如果除数在运行时为 0，会导致运行时恐慌。如果除数是负数并且除数是：\n\n```\n x     x / 4     x % 4     x \u003e\u003e 2     x \u0026 3\n 11      2         3         2          3\n-11     -2        -3        -3          1\n```\n\n位移运算符移动左侧运算元右侧元算元指定的位数。如果左侧是有符号整型，那它就实现了位移运算，如果是无符号整数使用逻辑位移。位移运算没有上限，位移操作让左边运算元位移 n 个 1。`x\u003c\u003c1` 和 `x*2` 是相等的。并且 `x\u003e\u003e1` 和 `x/2` 是相同的。\n\n对于整数运算元，一元运算符+-^定义如下：\n```\n+x                          is 0 + x\n-x    negation              is 0 - x\n^x    bitwise complement    is m ^ x  with m = \"all bits set to 1\" for unsigned x\n                                      and  m = -1 for signed x\n```\n\n###### 整型溢出\n\n对于无符号的值，运算符+-*和\u003c\u003c都是2禁止运算。这里的n是无符号类型的宽度，无符号整型将会丢弃溢出的位，并且程序将会返回`wrap around`。\n\n对于有符号的整数，操作符+=*\u003c\u003c都会溢出并且值存在，并且代表相应的有符号的值。在运算时不会抛出异常。标一起不会报错。所以不是所有情况下x\u003cx+1都成立。\n\n###### 浮点数运算符\n\n\n对于浮点数和其他复杂数字，+x和x是一样的，-x是x的对立面。除了IEEE-754还没有指定浮点数除0或者复数的结果。是否抛出异常将会依赖其具体实现。\n\n一种实现可以合并多个浮点操作进一个操作，有可能是夸语句的，并且他的结果可能和依次单独执行的结果不一样。1个浮点数类型将会转变成目标的精度，防止四舍五入的融合。\n\n\n```\n// FMA allowed for computing r, because x*y is not explicitly rounded:\nr  = x*y + z\nr  = z;   r += x*y\nt  = x*y; r = t + z\n*p = x*y; r = *p + z\nr  = x*y + float64(z)\n\n// FMA disallowed for computing r, because it would omit rounding of x*y:\nr  = float64(x*y) + z\nr  = z; r += float64(x*y)\nt  = float64(x*y); r = t + z\n```\n\n###### 字符串\n\n字符串可以使用+和+=操作符。\n\n```go\ns := \"hi\" + string(c)\ns += \" and good bye\"\n```\n\n字符串想家将会创建一个新的字符串。\n\n#### 比较运算符\n\n比较运算符比较连个运算元，并且生成一个无类型的布尔值。\n\n```\n==    equal\n!=    not equal\n\u003c     less\n\u003c=    less or equal\n\u003e     greater\n\u003e=    greater or equal\n```\n\n在任何比较运算元中2种类型必须是可以分配的。\n\n使用等于运算符`==`和`!=`的运算元必须是可比较的。使用顺序运算符`\u003c`,`\u003c=`,`\u003e`和`\u003e=`必须是可比较的。这些限制导致比较运算符被定义成以下的方式。\n\n* 布尔值是可比较的，两个布尔值当他们同为`true`或者false的使用是相等的\n* 整数值是可比较和排序的\n* 浮点数是可比较和排序的，具体定义在IEEE-754标准中。\n* 复数是可比较的，2个复数当实部和虚部都相等时就是相等的。\n* 字符串是可以比较和排序的。是按照字节顺序排序。\n* 指针式可以排序的，连个指针当指向相同变量时是相同的，或者他们2个都是nil。指向一个为非配的变量的结果是未定义的。\n* channel是可比较的。当两个管道是用同一个make出来的，或者都是nil时时相等的。\n* 接口值时可以比较的，2个接口值时相等的如果2个标识符的动态类型是一样的或者他们都是nil。\n* 一个非接口类型的值x和一个接口类型的值T在非接口类型是可以比较的并且非接口类型实现了接口是是可以比较的。当他们的动态类型类型相同时时相等的。\n* 当结构体内的所有字段都是可以比较的时候，他是可以比较的。连个结构体的值当非空字段都相等时他们是相等的。\n* 数组类型的值时可比较的，如果数组的原属时可以比较的，那么当数组的所有值是相等的时候他们就是相等的。\n\n使用两个动态类型的标识符来比较接口的值。如果这个类型的值时不可比较的，那么将会引起一个panic。这个行为不仅仅时接口，数组结构体接口字段都有这个问题。\n\n切片，map，和函数值都是不可比较的，然而，作为一个特殊的例子，切片，map和函数的值的nil时可以比较的，指针，channel和接口的值nil也是可以比较的。\n\n```\nconst c = 3 \u003c 4            // c is the untyped boolean constant true\n\ntype MyBool bool\nvar x, y int\nvar (\n\t// The result of a comparison is an untyped boolean.\n\t// The usual assignment rules apply.\n\tb3        = x == y // b3 has type bool\n\tb4 bool   = x == y // b4 has type bool\n\tb5 MyBool = x == y // b5 has type MyBool\n)\n```\n\n#### 逻辑操作符\n\n逻辑运算符使用布尔值值，并且生成一个相同类型的结果值作为操作元。右面的操作元计算是有条件的。\n\n```\n\u0026\u0026    conditional AND    p \u0026\u0026 q  is  \"if p then q else false\"\n||    conditional OR     p || q  is  \"if p then true else q\"\n!     NOT                !p      is  \"not p\"\n```\n\n#### 地址操作符\n\n以类型 T 的 x 作为运算元，取址操作 `\u0026x` 会生成一个类型为 `*T` 并指向 x 的指针。运算元必须是能够取址的，它可以是一个变量，指针，切片的取值操作；或是一个可取址结构体的字段选择器；或是对于可取址数组的索引取值操作。作为寻址能力的例外，x 可能是一个复合字面值。如果对 x 进行取址操作将会 panic，`\u0026x` 也会 panic。\n\n对于一个 `*T` 类型的运算元 x，指针解引用 `*x` 表示 x 指向的 T 类型。如果 x 为 nil，那么解引用 `*x` 会 panic。\n\n```go\n\u0026x\n\u0026a[f(2)]\n\u0026Point{2, 3}\n*p\n*pf(x)\n\nvar x *int = nil\n*x   // causes a run-time panic\n\u0026*x  // causes a run-time panic\n```\n\n#### 接收操作符\n\n对于管道类型的运算元 `ch`，接收操作 `\u003c-ch` 返回值是管道 `ch` 接收到的值。带方向的管道需要有接受权限，接收操作的类型也是通道的元素类型。表达式会一直阻塞直到接收到返回值。从 nil 通道接收值会一直阻塞。从一个已经关闭的通道接收数据会在其他数据都被接收以后生成该通道元素类型的零值。\n\n```go\nv1 := \u003c-ch\nv2 = \u003c-ch\nf(\u003c-ch)\n\u003c-strobe  // wait until clock pulse and discard received value\n```\n\n接收数据的表达式可以使用赋值表达式。\n\n```go\nx, ok = \u003c-ch\nx, ok := \u003c-ch\nvar x, ok = \u003c-ch\nvar x, ok T = \u003c-ch\n```\n\n它还可以生成一个额外的无类型布尔值来表示通道是否关闭。如果 ok 为 `true` 说明获取到的是发送到通道内的数据，而 `false` 它就返回一个零值因为通道内没有元素且已经关闭。\n\n#### 类型转换\n\n类型转换表达式 `T(x)` 其中 T 代表类型，x 代表可以转换成 T 类型的表达式。\n\n```\nConversion = Type \"(\" Expression [ \",\" ] \")\" .\n```\n\n如果类型是以 `*` 或 `\u003c-` 开头，或以关键字 `func` 开头并且没有返回值列表，那么它必须用括号括起来避免歧义：\n\n```go\n*Point(p)        // same as *(Point(p))\n(*Point)(p)      // p is converted to *Point\n\u003c-chan int(c)    // same as \u003c-(chan int(c))\n(\u003c-chan int)(c)  // c is converted to \u003c-chan int\nfunc()(x)        // function signature func() x\n(func())(x)      // x is converted to func()\n(func() int)(x)  // x is converted to func() int\nfunc() int(x)    // x is converted to func() int (unambiguous)\n```\n\n常量 x 可以在可以用类型 T 表示时自动转换。作为一个特例，整数常量 x 可以转换成字符串类型就和非常量 x 一样。\n\n对常量的转换会生成一个指定类型的常量。\n\n```go\nuint(iota)               // iota value of type uint\nfloat32(2.718281828)     // 2.718281828 of type float32\ncomplex128(1)            // 1.0 + 0.0i of type complex128\nfloat32(0.49999999)      // 0.5 of type float32\nfloat64(-1e-1000)        // 0.0 of type float64\nstring('x')              // \"x\" of type string\nstring(0x266c)           // \"♬\" of type string\nMyString(\"foo\" + \"bar\")  // \"foobar\" of type MyString\nstring([]byte{'a'})      // not a constant: []byte{'a'} is not a constant\n(*int)(nil)              // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type\nint(1.2)                 // illegal: 1.2 cannot be represented as an int\nstring(65.0)             // illegal: 65.0 is not an integer constant\n```\n\n非常量 x 可以在以下情况下转换成类型 T：\n\n* x 可以给类型 T 赋值\n* 忽略的结构体标签，x 的类型和 T 具有相同的底层类型\n* 忽略的结构体标签，x 的类型和 T 都是指针类型，并且指针所指的类型具有相同的底层类型\n* x 的类型和 T 都是整数或者浮点数类型\n* x 的类型和 T 都是复数类型\n* x 是一个字符串而 T 时字节切片或者 rune 切片\n\n在比较两个结构体类型的时候会忽略结构体标签：\n\n```go\ntype Person struct {\n\tName    string\n\tAddress *struct {\n\t\tStreet string\n\t\tCity   string\n\t}\n}\n\nvar data *struct {\n\tName    string `json:\"name\"`\n\tAddress *struct {\n\t\tStreet string `json:\"street\"`\n\t\tCity   string `json:\"city\"`\n\t} `json:\"address\"`\n}\n\nvar person = (*Person)(data)  // ignoring tags, the underlying types are identical\n```\n\n这个规则也适用于数字类型与字符串类型间的相互转换。这个转换可能会改变 x 的值并且会增加运行时消耗。包 unsafe 实现了这个功能底层的限制。\n\n###### 数字之间的转换\n\n对于非常量的数字转换，需要遵守以下规则：\n\n- 在转换整型数字时，如果是一个有符号整型，它是继承有符号的无限精度；否则就不用继承符号。转换时会截断数字以适应类型的大小。例如：如果 `v:=uint16(0x10F0)`，然后 ` `uint32(int8(v)) == 0xFFFFFFF0 。类型转换总是生成有效值，并且永远不会溢出。\n- 如果要将浮点数转换成整型，会丢弃小数部分（截断为零）。\n- 如果要将整型或浮点型转换成浮点数类型，或或者一个复数转换成其他复数类型，结果会四舍五入成指定精度。例如： 可以使用超出IEEE-754 32位数的附加精度来存储float32类型的变量x的值，但float32（x）表示将x的值舍入为32位精度的结果。x + 0.1 会使用超过 32 位的精度，而 `float32(x+0.1)` 不会。\n\n在所有浮点数和复数的非常量转换中，如果结构类型不能成功表示数据，那么结果将会依赖于具体平台实现。\n\n###### 字符串的类型转换\n\n1. 转换一个有符号或者无符号的整型值会转换成对应的 UTF-8 表示整型值。不在范围内的 Unicode 代码点会转换成 \"\\uFFFD\"。\n\n   ```go\n   string('a')       // \"a\"\n   string(-1)        // \"\\ufffd\" == \"\\xef\\xbf\\xbd\"\n   string(0xf8)      // \"\\u00f8\" == \"ø\" == \"\\xc3\\xb8\"\n   type MyString string\n   MyString(0x65e5)  // \"\\u65e5\" == \"日\" == \"\\xe6\\x97\\xa5\"\n   ```\n\n2. 将字节切片转换成字符串类型会生成一个由切片元素组成的字符串\n\n```go\nstring([]byte{'h', 'e', 'l', 'l', '\\xc3', '\\xb8'})   // \"hellø\"\nstring([]byte{})                                     // \"\"\nstring([]byte(nil))                                  // \"\"\n\ntype MyBytes []byte\nstring(MyBytes{'h', 'e', 'l', 'l', '\\xc3', '\\xb8'})  // \"hellø\"\n```\n\n3. 将 rune 切片转换成字符串类型会生成一个由切片元素组成的字符串\n\n```go\nstring([]rune{0x767d, 0x9d6c, 0x7fd4})   // \"\\u767d\\u9d6c\\u7fd4\" == \"白鵬翔\"\nstring([]rune{})                         // \"\"\nstring([]rune(nil))                      // \"\"\n\ntype MyRunes []rune\nstring(MyRunes{0x767d, 0x9d6c, 0x7fd4})  // \"\\u767d\\u9d6c\\u7fd4\" == \"白鵬翔\"\n```\n\n4. 将字符串转换成字节切片会生成由字符串中每个字节组成的切片\n\n   ```go\n   []byte(\"hellø\")   // []byte{'h', 'e', 'l', 'l', '\\xc3', '\\xb8'}\n   []byte(\"\")        // []byte{}\n   \n   MyBytes(\"hellø\")  // []byte{'h', 'e', 'l', 'l', '\\xc3', '\\xb8'}\n   ```\n\n5. 将字符串转换成 rune 切片会生成由字符串中每个 Unicode 代码点组成的切片\n\n```go\n[]rune(MyString(\"白鵬翔\"))  // []rune{0x767d, 0x9d6c, 0x7fd4}\n[]rune(\"\")                 // []rune{}\n\nMyRunes(\"白鵬翔\")           // []rune{0x767d, 0x9d6c, 0x7fd4}\n```\n\n###### 常量表达式\n\n常量表达式只包含常量运算元并且在编译程序时就已经计算完成。\n\n无类型布尔值，数值和字符串常量都可以当作运算元。除了位置操作符，如果二元运算符石不同类型的常量，操作元，和非布尔值，和即将在接下来出现的：整型，rune，浮点数和复数类型。例如：一个无类型整型常量减去无类型复数常量，结果为复数常量。\n\n一个常量的比较运算会生成无类型的布尔常量。如果左移运算是一个无类型常量，结果会是一个整型常量。它会和原来常量为相同类型。其他与无类型常量的运算都会生成相同类型的结果（布尔值，整型，浮点数，复数，字符串常量）。\n\n```go\nconst a = 2 + 3.0          // a == 5.0   (untyped floating-point constant)\nconst b = 15 / 4           // b == 3     (untyped integer constant)\nconst c = 15 / 4.0         // c == 3.75  (untyped floating-point constant)\nconst Θ float64 = 3/2      // Θ == 1.0   (type float64, 3/2 is integer division)\nconst Π float64 = 3/2.     // Π == 1.5   (type float64, 3/2. is float division)\nconst d = 1 \u003c\u003c 3.0         // d == 8     (untyped integer constant)\nconst e = 1.0 \u003c\u003c 3         // e == 8     (untyped integer constant)\nconst f = int32(1) \u003c\u003c 33   // illegal    (constant 8589934592 overflows int32)\nconst g = float64(2) \u003e\u003e 1  // illegal    (float64(2) is a typed floating-point constant)\nconst h = \"foo\" \u003e \"bar\"    // h == true  (untyped boolean constant)\nconst j = true             // j == true  (untyped boolean constant)\nconst k = 'w' + 1          // k == 'x'   (untyped rune constant)\nconst l = \"hi\"             // l == \"hi\"  (untyped string constant)\nconst m = string(k)        // m == \"x\"   (type string)\nconst Σ = 1 - 0.707i       //            (untyped complex constant)\nconst Δ = Σ + 2.0e-4       //            (untyped complex constant)\nconst Φ = iota*1i - 1/1i   //            (untyped complex constant)\n```\n\n对一个无类型整数，rune，或浮点数应用内置的 `complex` 函数会生成无类型的复数常量。\n\n```go\nconst ic = complex(0, c)   // ic == 3.75i  (untyped complex constant)\nconst iΘ = complex(0, Θ)   // iΘ == 1i     (type complex128)\n```\n\n常量表达式总是一个明确的值；中间值和常量自己可以比语言所支持的精度更高，下面的声明是合法的：\n\n```go\nconst Huge = 1 \u003c\u003c 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)\nconst Four int8 = Huge \u003e\u003e 98  // Four == 4                                (type int8)\n```\n\n常量的除法的除数不能为 0:\n\n```go\n3.14 / 0.0   // illegal: division by zero\n```\n\n定义了类型的常量的精度必须根据常量类型定义。所以下面的常量表达式是非法的：\n\n```go\nuint(-1)     // -1 cannot be represented as a uint\nint(3.14)    // 3.14 cannot be represented as an int\nint64(Huge)  // 1267650600228229401496703205376 cannot be represented as an int64\nFour * 300   // operand 300 cannot be represented as an int8 (type of Four)\nFour * 100   // product 400 cannot be represented as an int8 (type of Four)\n```\n\n补码使用的一元操作符 ^ 对于非常量的匹配模式：补码对于无符号常量为 1，对于有符号和无类型常量为 -1。\n\n```go\n^1         // untyped integer constant, equal to -2\nuint8(^1)  // illegal: same as uint8(-2), -2 cannot be represented as a uint8\n^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)\nint8(^1)   // same as int8(-2)\n^int8(1)   // same as -1 ^ int8(1) = -2\n```\n\n实现限制：编译器在处理无类型浮点数和复数时会取近似值；具体请看常量章节。这个取近似值的操作在浮点数在整数上下文时会产生无效值，即使在计算过后是一个整型。\n\n#### 运算优先级\n\n在包级别，初始化的依赖性由变量声明的初始化表达式顺序决定。否则，当计算表达式内的操作数时，赋值，返回语句，所有函数调用，方法调用，和通信操作都会由左向右计算。\n\n例如，在函数作用域中的赋值：\n\n```go\ny[f()], ok = g(h(), i()+x[j()], \u003c-c), k()\n```\n\n函数调用和通信的发生顺序为：`f()`，`h()`，`i()`，`j()`，`\u003c-c`，`g()` 和 `k()`。但是对 y 和 x 的取值操作没有指定。\n\n```go\na := 1\nf := func() int { a++; return a }\nx := []int{a, f()}            // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified\nm := map[int]int{a: 1, a: 2}  // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified\nn := map[int]int{a: f()}      // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified\n```\n\n在包级别，依赖的初始化顺序会覆盖这个从左向右的规则：\n\n```go\nvar a, b, c = f() + v(), g(), sqr(u()) + v()\n\nfunc f() int        { return c }\nfunc g() int        { return a }\nfunc sqr(x int) int { return x*x }\n\n// functions u and v are independent of all other variables and functions\n```\n\n## 语句\n\n语句控制程序的执行。\n\n```\nStatement =\n\tDeclaration | LabeledStmt | SimpleStmt |\n\tGoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |\n\tFallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |\n\tDeferStmt .\n\nSimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .\n```\n\n#### 终止语句\n\n终止语句会阻止相同代码块中下面所有语句的执行。以下语句属于终止语句：\n\n1. `return` 和 `goto` 语句\n2. 对内置 `panic` 函数的调用\n3. 代码块结束\n4. `if` 语句中：\n   1. else 分支\n   2. 所有分支末尾\n5. `for`语句中：\n   1. `break` 语句和循环结束\n6. `switch` 语句：\n   1. 在 `switch` 语句中没有 `break` 语句，\n   2. 有一个默认的 case\n   3. 语句列表中的每个 case 语句和有可能存在的 `fallthrough` 语句\n7. `select` 语句中：\n   1. 没有 break 语句\n   2. 每个 case 中的语句列表，如果包含默认 case\n\n所有其他语句都不是中断语句。\n\n如果语句序列不为空并且最后一个非空语句是终止语句，那么语句序列就以终结语句结尾。\n\n#### 空语句\n\n空语句不做任何事情。\n\n```\nEmptyStmt = .\n```\n\n#### 标签语句\n\n标签语句可以作为 `goto`，`break` 和 `continue` 语句的目标。\n\n```\nLabeledStmt = Label \":\" Statement .\nLabel       = identifier .\n```\n\n```\nError: log.Panic(\"error encountered\")\n```\n\n#### 表达式语句\n\n除了特定的内置函数，一般的函数、方法和接收操作都可以出现在表达式语句的上下文中。这些语句可以使用括号括起来。\n\n```\nExpressionStmt = Expression .\n```\n\n下面的内置函数不允许出现在语句的上下文中：\n\n```\nappend cap complex imag len make new real\nunsafe.Alignof unsafe.Offsetof unsafe.Sizeof\n```\n\n```\nh(x+y)\nf.Close()\n\u003c-ch\n(\u003c-ch)\nlen(\"foo\")  // illegal if len is the built-in function\n```\n\n#### 发送语句\n\n发送语句可以向通道发送一个值。通道表达式必须是通道类型，通道方向必须允许发送操作，并且值类型是可以分配给通道元素通道类型。\n\n```\nSendStmt = Channel \"\u003c-\" Expression .\nChannel  = Expression .\n```\n\n通道类型和值表达式会在发送之前求值。发送操作会一致阻塞，直到可以进行发送操作。如果接收者已经准备好向没有缓存的通道发送值可以立即执行。如果通道内还有缓存空间，向通道内发送值也会立即执行。向关闭的通道发送数据会导致运行时恐慌。像值为 nil 的通道发送数据会一直阻塞。\n\n```\nch \u003c- 3  // send value 3 to channel ch\n```\n\n#### 递增/递减语句\n\n“++” 和 “--” 语句可以递增或者递减运算元一个无类型常量 1。作为一个赋值语句，运算元必须是可寻址的或者 map 的索引表达式。\n\n```\nIncDecStmt = Expression ( \"++\" | \"--\" ) .\n```\n\n下面的赋值语句在语义上是等价的：\n\n```\nIncDec statement    Assignment\nx++                 x += 1\nx--                 x -= 1\n```\n\n#### 赋值\n\n```\nAssignment = ExpressionList assign_op ExpressionList .\n\nassign_op = [ add_op | mul_op ] \"=\" .\n```\n\n所有左侧运算元都必须是可寻址的、map 索引表达式或空标识符其中之一。运算元可以用括号括起来。\n\n```\nx = 1\n*p = f()\na[i] = 23\n(k) = \u003c-ch  // same as: k = \u003c-ch\n```\n\n对于赋值操作 `x op= y` 其中 `op` 为二元运算符，它和 `x=x op (y)` 是等价的，不过它只计算一次 x。`op=` 是单独的一个词汇单元，在赋值操作中左侧表达式和右侧表达式必须都是单值表达式，","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaberuster%2Fgo-language-specification","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaberuster%2Fgo-language-specification","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaberuster%2Fgo-language-specification/lists"}