Summary
Day01 是 2025 年 12 月 19 日写的,这篇是 2026 年 10 月 17 日。中间隔了十个月,跟"第二天"三个字已经没什么关系了。 所以标题里的 Day02 改成日期,不然越看越像自己骗自己。 翻出 A Tour of Go,接着上次的进度往下走。
For
Go 只有一种循环结构,即 for 循环。
最基本的 for 循环由三个部分组成,这三个部分之间使用分号分隔:
初始化语句(init statement):在第一次循环迭代之前执行。 条件表达式(condition expression):在每次循环迭代开始前进行判断。 后置语句(post statement):在每次循环迭代结束时执行。
初始化语句通常是一个简短的变量声明,在这里声明的变量只在该 for 语句的作用域内可见。
当布尔条件表达式的结果为 false 时,循环将停止执行。 注意:与 C、Java 或 JavaScript 等语言不同,Go 的 for 语句三个组成部分外部没有括号,同时代码块的大括号 { } 是必须存在的。
三个部分也不是必须都写。只留条件就是别的语言的 while,一个都不留就是死循环:
| |
我一开始还去找 Go 里 while 的关键字,翻了半天,原来根本没有这个东西。
If
if 和 for 一样,条件外面不要括号,但是 {} 必须有。它有个别的语言没有的用法:条件前面可以先跑一个短语句,在那里声明的变量只活在 if/else 里面。
| |
看着别扭,用两次就顺手了。先取再判断的场景不用在外面多留一个变量,省事。
Switch
switch 不需要写 break,匹配到就停。它甚至可以不写条件,那就等价于一串 if-else:
| |
switch 也支持那个前置短语句,switch n := 128; n { ... } 这样写,少一个变量名。
Defer
defer 是把这一句推迟到函数返回之前才执行,多个 defer 是后进先出,也就是栈的顺序。
| |
实际用的时候基本都是成对出现的:打开文件就 defer 关闭,加锁就 defer 解锁。写的时候不用记什么时候关,放在打开旁边就行,不会漏。
把这几段凑成一个 main 跑一下:
指针
Go 有指针,但没有指针运算。
| |
那句 p.X 是这里让我觉得最舒服的地方。C 里得写 (*p).X 或者 p->X,Go 直接帮你把箭头省略了。
数组和切片
[5]int 是数组,长度属于类型的一部分,是值类型——赋值一次就是整体复制一份。
[]int 是切片,日常写的几乎都是它。它背后是一个数组,自己带长度和容量:
| |
这里有个坑我特意试了一下:切出来的那一段和原切片共享同一块底层数组,改子切片会把原切片一起改掉。
| |
不是复制,是真共享。官方有一篇专门讲这个的 Slices: usage and internals,我只看了开头,感觉得单独找时间读一遍,现在只能说"知道有这么回事"。
Map
map 就是别的语言的哈希表/字典,写法是这样的:
| |
取一个不存在的键不会报错也不会 panic,返回零值和 false。所以判断"有没有"要用两个返回值这种写法。这个设计比抛异常舒服得多,代价是得记着去判断 ok。
再来一遍,看看结果:
函数值和闭包
Go 里函数是一等公民,可以当变量传,也可以当返回值:
| |
闭包会牢牢记住外层那个变量,不是复制一份带走。所以每次调用返回的那个函数,累加的其实是同一个 sum。
先到这里
再往下就是方法和接口了,后面还有 goroutine 和 channel。那部分是 Go 真正有意思的地方,也是真正容易写错的地方。
不过今天先这样,A Tour of Go 前面这几节过了一遍,代码也都跑通了。等方法接口那节真用到的时候再回来补,反正笔记是给自己看的,不用一次写完。

