编程语言研究书库

Go 的语法、变量与数据类型 — 一套刻意做小的类型系统

Go 的语法、变量与数据类型 — 一套刻意做小的类型系统

这一章讲什么: Go 程序由哪些最小零件组成——包、变量、常量、十几种数据类型、 输入输出——以及每个零件设计时「删掉了什么」。 它在全书链条里的位置: 这是地基章。第 03 章在这堆零件之上讲「怎么把它们组织成 模块」,第 04 章讲「出错怎么办」;后面所有章节(API、数据库、并发流水线)都默认你 已经会这里的每一个零件。 读法: 如果你学过别的语言,别跳——Go 在每个看似熟悉的地方都藏了一个有意的 「不一样」,本章每一节的「解决什么问题」就是在讲这个不一样。

1. 顶层全景:一个程序的最小骨架

原书给的最小 Go 程序只有三行(第 01 章也出现过的那个):

package main

import "fmt"

func main() {
    fmt.Println("Hello, Readers!")
}

这三行对应三件不可少的事,后面所有 Go 代码都是它们的展开:

零件作用本章哪节展开
package main声明这个文件属于哪个包;main 这个包名特指「可执行程序」§2
import "fmt"引入别人的代码给自己用§2
func main()程序入口,从这里开始执行第 03 章细讲函数

主线例子(贯穿本章): 一个「用户注册」小程序。它读入一行用户名和年龄, 存进一条用户记录,再打印一份统计。每一步都用本章一个机制:

  读入名字和年龄 ──► 存成 struct(一条记录) ──► 放进 map(按名字查) ──► 用 slice 存多条
  ──► 打印汇总(Printf) ──► 演示零值与类型转换

这个例子是我们顺着原书各节的示例拼起来的(原书各机制分开举例,没有这样一个完整程序); 每个机制的原书出处都在脚注里。

2. 包、import 与 gofmt:先统一格式,再谈风格

2.1 包是什么

Go 把代码组织成包(package):每个 .go 文件第一行必须声明自己属于哪个包, 一组同包的文件一起编译1。包有两个作用:一是分工——网络代码一个包、加密代码一个包; 二是复用——标准库就是一群官方包,import "net/http" 一行就把整个 HTTP 包的能力 拿过来2。

import 可以单写,也可以分组:

import (
    "fmt"
    "strings"
    "time"
)

原书强调这带来一个文化后果:Go 项目靠包天然分层,大型代码库因此「clear modularization」3。

2.2 gofmt:不让任何人讨论格式

解决什么问题: 别的语言里,「大括号换不换行」「用 Tab 还是空格」能吵十年, 代码评审一半时间在吵格式。

Go 的做法: 官方工具 gofmt 把格式定死并自动执行:大括号必须和控制语句同一行、 缩进必须用 Tab、行尾分号由编译器自动补——你基本见不到也无需写分号4。 它不是建议,是事实标准:编辑器存盘时自动跑一遍,所有人的代码长得一样。

为什么有效: 格式的「选择权」被没收,换来读陌生人代码时零适应成本。 这一条和第 01 章「标准库厚」是同一个哲学:能统一的不留给个人发挥。

2.3 控制流:三样,没有更多

Go 的全部控制流是三样:if、for、switch——原书明确说 Go 「trims away unnecessary complexity」,循环只有 for 一种5:

if value > 10 {
    fmt.Println("Large value")
}
for i := 0; i < 5; i++ {
    fmt.Println(i)
}
switch day {
case "Monday":
    fmt.Println("Start of week")
default:
    fmt.Println("Midweek or weekend")
}

两个有意的「不一样」:

  • if 的条件不带括号。这不是偷懒,是 gofmt 规定的统一写法。
  • switch 默认不会贯穿到下一个 case(C 语言会的那个坑被默认关掉了;真想贯穿要写 fallthrough 关键字)5。

3. 变量与常量:两种声明,三个坑

3.1 两种声明法

Go 给变量两种写法,原书并列给出6:

var count int          // 显式声明:类型说在名字后面
var name string = "Go" // 显式声明带初值
count := 10            // 简写声明:类型由右边的值推出来

注意 var count int 里类型写在变量名后面——Go 的惯例和 C 相反。简写 := 只能在 函数内部用,函数外面必须用 var7。

主线例子第 ① 步: 注册程序开头:

var appName string = "user-register"
maxUsers := 1000

3.2 常量与它的两个变种

常量用 const 声明,值必须在编译时就能确定——运行时才算得出来的东西 (比如函数调用的结果)不能当常量7。Go 还细分:

  • 带类型常量:const limit int = 100,类型钉死,参与运算时被检查;
  • 无类型常量:const limit = 100,编译器按使用场合灵活解释——用在浮点运算里就当浮点。

原书说这种双轨让常量「既 predictable 又 convenient」8。

3.3 三个坑(原书明列)

  1. := 不能用在包级(函数外),新手常在这里摔7;
  2. 运行期产生的值不能赋给常量7;
  3. 声明了不用的变量是编译错误——不是警告,是直接编译不过7。 这一条最反直觉也最有用:它强制你删掉写歪了的草稿,垃圾不会留到代码库里。

4. 基本类型:数字、布尔、字符串

4.1 数字类型:一张按位宽分的表

Go 的数字类型按「占多少位、带不带符号」命名,原书照规范给了完整表9:

类型范围什么时候用
int8 / uint8−128…127 / 0…255明确知道数值很小
int32 / uint32约 ±21 亿 / 0…约 42 亿与文件格式、网络协议对齐时
int64 / uint64约 ±92 亿亿时间戳、大计数
float32 / float64IEEE 754 浮点小数;float64 是默认
byteuint8 的别名原始字节
runeint32 的别名一个 Unicode 字符(Unicode:给全世界每个字符统一编号的国际标准)
int / uint32 或 64 位,看机器日常默认

两个别名要记住:byte 就是 uint8,rune 就是 int32——看见别人的代码里写 rune 不要陌生9。int 的大小随机器变(32 位机器上 32 位、64 位机器上 64 位), 做可移植计算时要注意——这是其他语言用户容易默认错的一点。

主线例子第 ⑥ 步用到的细节: 年龄用 int,余额类小数用 float64—— 不能用 int 存小数,那会把 0.1 直接变成 0(原书在 AI 建议一节拿这个当反面例子10)。

4.2 布尔

bool,两个值 true / false,没有别的真值(不能把 0 或 1 当布尔用)9。

4.3 字符串:不可改的字节序列

字节(byte)是 8 个二进制位组成的一个 0–255 的整数,文件、网络数据最原始的形态;序列就是排成一队。Go 的 string 是一串字节,不可变——「once they are generated, their contents cannot be altered」11。两个直接后果:

  • len(s) 返回的是字节数,不是字符数:"你好" 的 len 是 6(每个汉字 3 字节), 不是 2;
  • 想「改」字符串只能造一个新的。配合 []byte(s) 可以转成字节切片改完再转回来12。

5. 复合类型:从数组到 channel

这一节是全章重心,七种复合类型每种回答「解决什么存取问题」。

5.1 数组:定长, rarely 直接用

数组是定长的同类型序列,长度是类型的一部分:

var scores [5]int        // 5 个 int,初值全是 0
var grid [3][5]int       // 多维:3 行 5 列

「长度是类型的一部分」意味着 [3]int 和 [5]int 是两种不同的类型,不能互换。 实际开发里数组几乎总被下面的切片取代;数组还留着一个重要规则:不能直接嵌套自己 ([10]T1 里装 T1 是非法的,装 *T1 指针才合法——指针就是「存另一个值的地址」的变量,§5.5 专讲——结构里不能有无限大的自己)13。

5.2 切片:数组的「窗口」,Go 里真正的主角

解决什么问题: 数组定长太死——读一个文件,行数事先不知道,数组装不下。

切片(slice)是「指向某段底层数组的窗口」:它记三件事——从哪开始、多长、 底层数组容量多大。所以切片可以变长(用 append 追加),可以互相重叠地 看同一段数据14。创建方式:

names := make([]string, 0, 16)   // 0 个元素,预留容量 16
names = append(names, "alice")

原书给了一个精确等价:make([]int, 50, 100) 就是 new([100]int)[0:50]—— 先造一个 100 的数组,再取前 50 个当窗口15。注意「共享存储」:两个切片若看着 同一个底层数组,通过一个改,另一个也看得见;独立的数组之间则互不影响14。 这是 Go 新手最常踩的坑之一,第 04 章的调试工具能帮上忙。

主线例子第 ④ 步: 所有注册用户的名字存进切片,随时 append。

5.3 结构体:把几个字段捆成一条记录

结构体(struct)是有名字段的集合,Go 里「对象」的替代物(没有类,第 03 章细说):

type User struct {
    ID     int
    Name   string
    Age    int
    Active bool
}

原书还讲了嵌入字段(embedded field):只写类型名不写字段名,那个类型的字段和 方法会被「提升」到外层——这是 Go 实现组合(而非继承)的手法,第 03 章细讲16。

主线例子第 ② 步: 每个注册用户就是一条 User 记录。

5.4 map:按 key 查的字典

map 是无序的 key→value 集合,map[string]int 读作「字符串 key、整数值」:

ages := make(map[string]int)
ages["alice"] = 30

对 map 取值有两个返回值的写法 age, ok := ages["alice"]——ok 告诉你这个 key 在不在(不在时 age 是该类型的零值 0,光看值分不出「没这个人」和「这个人 0 岁」)。

两个坑(原书明写):key 的类型必须支持 == 比较——函数、map、切片不能当 key; nil map 不能写入——只声明 var m map[string]int 就 m["x"]=1 会 panic (运行时崩溃),必须先 make17。

主线例子第 ③ 步: 用 map[string]User 按名字查用户。

5.5 指针:地址,不是魔法

指针(pointer)存的是另一个值的地址。Go 保留了指针但砍掉了指针算术:

x := 10
p := &x    // p 的类型是 *int,存 x 的地址
*p = 20    // 通过 p 改 x,x 现在是 20

什么时候用:函数想改调用者的变量(传地址而不是复制一份),或者大结构体想避免 整份拷贝。没初始化的指针是 nil,解引用 nil 会 panic——这是第 04 章的主角之一18。

5.6 函数类型与接口:行为也是类型

函数类型把「签名一致的函数」归为一类,变量可以存函数、函数可以当参数传19。 这听着抽象,第 07 章的中间件全靠它。

接口(interface)是「方法清单」:一个类型只要实现了清单上的全部方法,就自动满足 这个接口,不需要显式声明「我实现了」(这叫隐式实现)20:

type Locker interface {
    Lock()
    Unlock()
}

接口三种形态,原书按从简到繁排:

形态是什么例子
基本接口只有方法清单interface{ String() string }
嵌入接口把已有接口并进来ReadWriter = Reader + Writer
广义接口能用 | 列出具体类型,还能写 ~int 表示「底层类型是 int 的都算」泛型约束,第 03 章一带而过

空接口 interface{}(Go 1.18 起有个更好读的别名 any)表示「任何类型」—— 它能装一切,但取出来要做类型断言,否则没法用21。

接口的零值是 nil——一个 nil 接口调用方法会 panic,这条第 04 章展开。

5.7 channel 类型:连类型都带方向

channel 本身也是一种类型,而且能声明方向:

chan int        // 能发能收
chan<- float64  // 只能发(写)
<-chan int      // 只能收(读)

方向是编译期检查的:把一个「只能收」的 channel 传给要「发」的函数,编译直接报错。 这个设计让函数签名自带「我只会往这个 channel 里放数据」的合同,读代码的人不用猜22。 channel 是第 05 章的主角,这里记住两件事:它由 make 创建,可带缓冲容量; close 关闭后,接收方用 v, ok := <-ch 能收到「已关闭」的信号(ok 为 false)22。

5.8 七种复合类型,一张选型总表

学完之后回头看,这七种的区分标准就三个:定长还是变长、按位置还是按键找、 能不能并发安全地共享。对着主线例子的四个用场:

需求选一句话理由
名字列表,越注册越多slice变长,append 就行
按名字查整条记录map按键找,O(1)
一条记录本身struct字段有名字、有类型
函数想改调用者的变量传指针 *T值传递改不了外面
函数当参数/返回值函数类型行为也是值
「任何类型都收」any空接口,取出再断言
goroutine 之间传channel传送带,带方向

三个回头看的提醒:

  • 数组在这个表里没有位置——不是它没用,而是它的活(定长缓冲、协议头) 要到第 05、08 章才显形;日常数据容器就是切片;
  • map 和 slice 都不要提前担心并发——第 05 章会说,默认它们都不安全, 要么走 channel,要么加锁;
  • 选 struct 还是 map 的判断很简单:字段是固定的就 struct(编译器帮你检查 拼写),字段是动态的就 map(运行时再查)。

5.9 广义接口:type set(类型集)是怎么算的

第 02 章给过广义接口的式子(int | ~int 那种),这里把规则算一遍, 因为这是 Go 泛型的门槛,也是「接口」这个词最不像 Java 接口的地方。

类型集的含义:一个接口「能装哪些类型」的集合。逐条规则(原书照规范列了七条, 收成三句):

  1. 写一个具体类型(如 int)——集合里只有它自己;
  2. 写 ~int——集合里是「底层类型是 int 的所有类型」(int 自己,以及 type MyInt int 这种自定义类型);
  3. 用 | 并列几项——取并集;交集为空就是空集(如 int | string 再加方法 约束,没有类型同时满足,编译直接拒绝)。

例子(演示):

interface { ~int }                    // 底层是 int 的都算:MyInt 也算
interface { int }                     // 只有 int:MyInt 都不算
interface { ~int; String() string }   // 底层是 int、且带 String 方法的

any 是空接口的别名(第 02 章),它的类型集是全部非接口类型——所以 any 能装一切,也正因如此,从 any 里取东西必须类型断言 (「我断言这个 any 里装的是 string」,断言错了运行期 panic)。 这条链决定了 Go 泛型函数的样子:约束写接口,函数体里对这个类型集能做的事 就只有约束里声明的那些——类型集是泛型的合同。

5.10 结构体嵌入:方法「提升」的边界

嵌入字段(只写类型名不写字段名)带来的「提升」规则,原书按规范给了边界:

  • 外层结构体直接「拥有」内嵌类型的字段和方法,调用 x.f 时编译器自动展开成 x.T.f;
  • 提升只跨一层规则清晰、多层同名会有歧义:两个内嵌类型都有方法 Close 时, 外层调 x.Close() 编译报错,必须写全 x.A.Close()——歧义是编译期错误,不是 运行期惊喜;
  • 内嵌 接口 也合法:结构体嵌入 io.Reader,自己就实现了 Read—— 这是组合模式在 Go 里的标准写法(第 07 章的 ResponseWriter 包装就用它)。

6. 零值、类型推导与转换:Go 的「默认值」哲学

6.1 零值:不初始化也有确定的值

Go 没有「未初始化的随机值」。任何变量没给初值,就自动拿到它类型的零值23:

类型零值
数字0
布尔false
字符串""(空串)
指针、接口、切片、map、channel、函数nil

nil 大致是「什么都没指向」的标记。这张表要背下来的一半原因是:nil 检查 在 Go 代码里无处不在(「这个 map 是不是 nil」「这个接口是不是 nil」), 另一半原因是零值设计让很多初始化代码可以直接省掉。

6.2 类型推导与「不做隐式转换」

:= 让编译器从右边推类型:total := 42 是 int,ratio := 3.14 是 float6424。 但推导只发生一次——声明之后,Go 绝不做隐式类型转换:

var x int = 10
var y float64 = float64(x)   // 必须显式写 float64(x)

把 int 直接赋给 float64 变量是编译错误,哪怕「明明装得下」25。 这和其他语言(尤其 Python 和 C)相反:那边默默帮你转,转错了运行时才炸; Go 逼你当场写清,换来「行为总是 explicit and predictable」25。 string 和 []byte 互转也要显式:b := []byte(s)25。

主线例子第 ⑤ 步: 打印统计时,总人数 len(names) 是 int, 若要拼进浮点算平均,得先 float64(len(names))。

7. 输入输出:fmt 求方便,bufio 求快

7.1 fmt:打印与简单读入

  • fmt.Println 输出并换行;fmt.Printf 用格式动词拼串:%d 整数、%s 字符串、 %f 浮点、%v 「任何值按默认样子打」、%t 布尔26;
  • 读入:fmt.Scanln(&name) 读一行——注意参数要传地址 &name, 因为函数要往里写。Scan 系适合「问用户一个问题」,不适合大批量数据27。

7.2 bufio:批量与逐行

bufio 在底层读取器外面包一层缓冲,攒一批再读写,系统调用次数大减28:

scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
    line := scanner.Text()
    fmt.Println("Received:", line)
}

Scanner 默认按行切,也能换成按词切(bufio.ScanWords)或自定义切法—— 读日志、读配置、做命令行交互都用它。写侧对应 bufio.Writer,写完要调 Flush() 把缓冲区里的内容真正推出去28。

原书给了一张选择表,值得直接记住29:

需求用
快速打印、调试用fmt.Println
结构化输出fmt.Printf
简单用户输入fmt.Scan*
高性能读写bufio.Reader / bufio.Writer
逐行文本处理bufio.Scanner

主线例子第 ① 步的读入因此选 bufio.Scanner 而不是 fmt.Scanln—— 注册程序要逐行处理输入。

8. AI 辅助写类型(辅线)

原书在本章末尾给了一组「让 AI 干这类活」的用法30:

  1. 生成声明: 打「创建一个存用户年龄的整型变量,初值 18」,得 age := 18;
  2. 类型建议: 给它看数据结构描述,它建议 float64 还是 struct;
  3. 零值利用: 让它写「默认值安全」的声明;
  4. 一致性重构: 把多个单行 var 合成组、建议 :=、揪出未使用变量。

本书的立场(第 01 章已定):这些是省键盘的活,判断选型的责任仍在人—— AI 不会替你想「年龄该用 int 还是 uint8」,它只会给你最常见答案。 反过来,让 AI 解释一段含切片的代码「这两个变量是不是看着同一个数组」, 是很好的自学用法(它答完你再对着 §5.2 核对)。

9. 作者的判断与证据

是语言事实的(可对照官方规范验证):

  • 零值表、显式转换、nil map 写入 panic、int 位数随机器——这些是 Go 规范定义的行为, 原书的表述与 go.dev/ref/spec 一致(原书多处注明「Source: go.dev/ref/spec」)92325。
  • 复合类型自嵌套限制、接口类型集规则——直接引自规范13。

是作者观点的:

  • 「格式化减少认知负荷」「三样控制流足够」——设计哲学叙述,无法证伪但对读代码确实有效;
  • 本章 AI 一节通篇是「可以怎么用来提效」,没有给对照数据;「avoid subtle bugs caused by inappropriate type choices」这类收益是合理推测,不是测量结论10。

判断(我们的,不是书里的): 这一章把规范内容嚼碎讲,适合零基础; 但对有经验的开发者,最有价值的是那三处「有意的不同」——声明不用会编译错、 不做隐式转换、nil map 写入 panic。它们全是「把一类运行期错误挪成编译期或显式失败」, 这是 Go 可靠性的真正来源,比「简单」这个形容词具体得多。 如果错,会错在: 若把「Go 强制显式」读成「Go 不会类型错」就错了—— 接口 nil、类型断言失败、切片共享,都还是运行期坑,第 04 章正为此存在。

10. 边界与局限

  • 本章教「有哪些零件」,不教「怎么组合」——组合是第 03 章(函数与包)以后的事。
  • 泛型几乎没展开: 广义接口只按规范介绍一遍,没有「什么时候该写泛型函数」的实战指导。
  • 例子以语法演示为主,离真实系统有距离;读者要自己完成「从练习题到项目」的跨越(第 06–08 章补)。
  • 性能一笔带过(bufio 为什么快只有一段),第 08 章的 pprof 和第 10 章的性能调优才补上。

11. 可带走的

  1. 程序三骨架:package main + import + func main();包是编译与分工的单位。
  2. var 与 := 两种声明;:= 仅限函数内;不用的变量编译不过。
  3. 常量编译期定值,分带类型/无类型;运行期的值当不了常量。
  4. 数字按位宽命名,byte=uint8、rune=int32;int 位数随机器;小数用 float64。
  5. 字符串是字节序列、不可变,len 数的是字节。
  6. 数组定长且长度是类型;切片是窗口、可 append、多切片可共享底层数组。
  7. nil map 不能写;取 map 用 v, ok 双返回值。
  8. 零值表:数字 0、布尔 false、字符串 ""、其余 nil。
  9. 不做隐式转换:int→float64 必须写 float64(x)。
  10. 输出 fmt、批量 bufio;按原书的选择表挑。
  11. channel 类型带方向(chan<- 只发、<-chan 只收),方向错编译期就拦。
  12. 接口是方法清单、隐式实现;any 是空接口别名。

12. 原文地图

主题原书章原文位置
包与 import2. Go Syntax, Variables, and Data Typestext/12-ch02-2-go-syntax-variables-and-data-types.txt:75(搜「Packages and Imports」)
gofmt 四条规则同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:91(搜「Formatting Philosophy」)
三样控制流同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:105(搜「Control Structures and Flow」)
两种变量声明同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:128(搜「Variables and Constants」)
三个坑同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:153(搜「Common Pitfalls」)
数字类型表同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:170(搜「Numeric Types」)
字符串不可变同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:218(搜「immutable」)
数组与自嵌套限制同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:231(搜「Array Types」)
切片与 make 等价式同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:271(搜「Slice Types」)text/12-ch02-2-go-syntax-variables-and-data-types.txt:291(搜「make([]int, 50, 100)」)
结构体与嵌入字段同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:299(搜「Struct Types」)
map 与 nil map同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:354(搜「Map Types」)
指针同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:382(搜「Pointer Types」)
接口三形态同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:418(搜「Interface Types」)、:467(搜「Embedded Interfaces」)、:499(搜「General Interfaces」)
any 别名同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:559(搜「alias for interface{}」)
channel 类型与方向同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:564(搜「Channel Types」)
零值表同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:616(搜「Zero Values」)
显式类型转换同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:631(搜「Type Conversion」)
fmt 与 bufio同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:646(搜「fmt and bufio」)
包选择表同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:724(搜「Choosing between fmt and bufio」)
AI 辅助生成声明同上text/12-ch02-2-go-syntax-variables-and-data-types.txt:740(搜「AI-Assisted Code Generation」)

Footnotes

  1. 出处:「2. Go Syntax, Variables, and Data Types」第 75 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:75,搜「Packages and Imports」)。原文:「Each Go file is a part of a package, which aids in organizing similar features.」 ↩

  2. 出处:同章第 80 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:80,搜「import keyword」)。示例 import "net/http" 见同段。 ↩

  3. 出处:同章第 89 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:89,搜「clear modularization」)。 ↩

  4. 出处:同章第 91–101 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:92,搜「strict opinion on formatting」;text/12-ch02-2-go-syntax-variables-and-data-types.txt:97,搜「Mandatory curly braces」)。四条规则:大括号同行、Tab、自动分号、统一空白。 ↩

  5. 出处:同章第 105–124 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:107,搜「three primary constructs」;text/12-ch02-2-go-syntax-variables-and-data-types.txt:110,搜「fall through」)。三个例子在同段。 ↩ ↩2

  6. 出处:同章第 135–143 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:135,搜「two primary forms」)。 ↩

  7. 出处:同章第 153–159 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:153,搜「Common Pitfalls」)。三个坑逐条列于原文。 ↩ ↩2 ↩3 ↩4 ↩5

  8. 出处:同章第 145–152 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:145,搜「Typed and Untyped Constants」)。 ↩

  9. 出处:同章第 170–213 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:170,搜「Numeric Types」;别名见 :199,搜「alias for uint8」)。 ↩ ↩2 ↩3 ↩4

  10. 出处:同章第 763–776 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:764,搜「recommending the most appropriate data type」;「using int instead of float64」见 :775)。 ↩ ↩2

  11. 出处:同章第 215–220 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:217,搜「series of bytes」;不可变见 :218,搜「immutable」)。 ↩

  12. 出处:同章第 637–640 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:637,搜「conversions between byte slices」)。 ↩

  13. 出处:同章第 247–268 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:248,搜「may not directly or indirectly contain」)。「Source: go.dev/ref/spec」为原书自注。 ↩ ↩2

  14. 出处:同章第 271–282 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:279,搜「underlying array」;共享存储见 :281,搜「shares storage」)。 ↩ ↩2

  15. 出处:同章第 289–292 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:283,搜「make, which takes a slice type」)。等价式 make([]int, 50, 100) 与 new([100]int)[0:50] 见同段。 ↩

  16. 出处:同章第 317–330 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:317,搜「embedded field」;提升规则见 :329,搜「promoted」)。 ↩

  17. 出处:同章第 354–380 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:361,搜「comparison operators」;nil map 见 text/12-ch02-2-go-syntax-variables-and-data-types.txt:380,搜「none of the elements can be appended」)。 ↩

  18. 出处:同章第 382–386 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:382,搜「Pointer Types」;「An uninitialized pointer holds a value of null」)。 ↩

  19. 出处:同章第 389–416 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:389,搜「Function Types」)。 ↩

  20. 出处:同章第 418–465 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:419,搜「delineates a group of types」;Locker 例见 :462,搜「Locker」)。 ↩

  21. 出处:同章第 559–562 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:559,搜「alias for interface{}」)。 ↩

  22. 出处:同章第 564–597 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:570,搜「The optional <- operator」;make 与 close 见 text/12-ch02-2-go-syntax-variables-and-data-types.txt:586,搜「make(chan int, 10)」)。 ↩ ↩2

  23. 出处:同章第 616–629 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:618,搜「automatically initialized to zero」)。零值表见 :620–624。 ↩ ↩2

  24. 出处:同章第 605–614 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:606,搜「inferring types」)。三个推导例子见同段。 ↩

  25. 出处:同章第 632–644 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:632,搜「requires explicit type conversion」;「compile-time error」见 :641)。 ↩ ↩2 ↩3 ↩4

  26. 出处:同章第 656–671 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:656,搜「Formatted Output」;动词表见 :670,搜「%s」)。 ↩

  27. 出处:同章第 673–681 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:674,搜「Scan, Scanln」)。 ↩

  28. 出处:同章第 683–722 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:683,搜「Efficient Input」;Flush 见 :708,搜「Flush」)。 ↩ ↩2

  29. 出处:同章第 724–734 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:724,搜「Choosing between fmt and bufio」)。表 2.3 见同段。 ↩

  30. 出处:同章第 740–818 段(text/12-ch02-2-go-syntax-variables-and-data-types.txt:740,搜「AI-Assisted Code Generation for Data Types」;自然语言例子见 :754,搜「user's age」)。 ↩

On this page