前言

起源

由下图可以粗略看出,许多早期的编程语言对Go语言的设计产生了重要影响。

  • C语言

    Go语言有时候被描述为“类C语言”,其从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想,还有C语言一直所在意的编译后机器码的运行效率以及和现有操作系统的无缝适配。

  • Pascal -> Modula-2 -> Oberon -> Oberon-2

    其中一个有影响力的分支来自Niklaus Wirth所设计的Pascal语言。Modula-2语言激发了包的概念。然后Oberon语言摒弃了模块接口文件和模块实现文件之间的区别。第二代的Oberon-2语言直接影响了包的导入和声明的语法,还有Oberon语言的面向对象特性所提供的方法的声明语法等。

  • CSP -> Squeak -> Newsqueak -> Alef

    [CSP] - 来自于贝尔实验室的Tony Hoare于1978年发表的鲜为外界所知的关于并发研究的基础文献 顺序通信进程communicating sequential processes),缩写为CSP。在CSP中,程序是一组中间没有共享状态的平行运行的处理过程,它们之间使用管道进行通信和控制同步。不过Tony HoareCSP只是一个用于描述并发性基本概念的描述语言,并不是一个可以编写可执行程序的通用编程语言。

    [Squeak] - Rob Pike和其他人开始不断尝试将CSP引入实际的编程语言中。他们第一次尝试引入CSP特性的编程语言叫Squeak(老鼠间交流的语言),是一个提供鼠标和键盘事件处理的编程语言,它的管道是静态创建的。

    [Newsqueak] - 改进版的Newsqueak语言提供了类似C语言语句和表达式的语法和类似Pascal语言的推导语法。Newsqueak是一个带垃圾回收的纯函数式语言,它再次针对键盘、鼠标和窗口事件管理。但是在Newsqueak语言中管道是动态创建的,属于第一类值, 可以保存到变量中。

    [Alef] - 在Plan9操作系统中,这些优秀的想法被吸收到了一个叫Alef的编程语言中。Alef试图将Newsqueak语言改造为系统编程语言,但是因为缺少垃圾回收机制而导致并发编程很痛苦。在Aelf之后还有一个叫Limbo的编程语言,Go语言从其中借鉴了很多特性。

  • 其他特性

    Go语言的其他的一些特性零散地来自于其他一些编程语言;

    比如iota语法是从APL语言借鉴,词法作用域与嵌套函数来自于Scheme语言(和其他很多语言)。当然,我们也可以从Go中发现很多创新的设计。比如Go语言的切片为动态数组提供了有效的随机存取的性能,这可能会让人联想到链表的底层的共享机制。还有Go语言新发明的defer语句。

语言设计项目

所有的编程语言都反映了语言设计者对编程哲学的反思,通常包括之前的语言所暴露的一些不足地方的改进。Go项目是在Google公司维护超级复杂的几个软件系统遇到的一些问题的反思(但是这类问题绝不是Google公司所特有的)。

正如Rob Pike所说,“软件的复杂性是乘法级相关的”,通过增加一个部分的复杂性来修复问题通常将慢慢地增加其他部分的复杂性。通过增加功能、选项和配置是修复问题的最快的途径,但是这很容易让人忘记简洁的内涵,即从长远来看,简洁依然是好软件的关键因素。只有通过简洁的设计,才能让一个系统保持稳定、安全和持续的进化。

Go项目包括编程语言本身,附带了相关的工具和标准库,最后但并非代表不重要的是,关于简洁编程哲学的宣言。就事后诸葛的角度来看,Go语言的这些地方都做的还不错:拥有自动垃圾回收、一个包系统、函数作为一等公民、词法作用域、系统调用接口、只读的UTF8字符串等。但是Go语言本身只有很少的特性,也不太可能添加太多的特性。例如,它没有隐式的数值转换,没有构造函数和析构函数,没有运算符重载,没有默认参数,也没有继承,没有泛型,没有异常,没有宏,没有函数修饰,更没有线程局部存储。但是,语言本身是成熟和稳定的,而且承诺保证向后兼容:用之前的Go语言编写程序可以用新版本的Go语言编译器和标准库直接构建而不需要修改代码。

Go语言有足够的类型系统以避免动态语言中那些粗心的类型错误,但是,Go语言的类型系统相比传统的强类型语言又要简洁很多。虽然,有时候这会导致一个“无类型”的抽象类型概念,但是Go语言程序员并不需要像C++或Haskell程序员那样纠结于具体类型的安全属性。在实践中,Go语言简洁的类型系统给程序员带来了更多的安全性和更好的运行时性能

Go语言鼓励当代计算机系统设计的原则,特别是局部的重要性。它的内置数据类型和大多数的准库数据结构都经过精心设计而避免显式的初始化或隐式的构造函数,因为很少的内存分配和内存初始化代码被隐藏在库代码中了。Go语言的聚合类型(结构体和数组)可以直接操作它们的元素,只需要更少的存储空间、更少的内存写操作,而且指针操作比其他间接操作的语言也更有效率。由于现代计算机是一个并行的机器,Go语言提供了基于CSP的并发特性支持。Go语言的动态栈使得轻量级线程goroutine的初始栈可以很小,因此,创建一个goroutine的代价很小,创建百万级的goroutine完全是可行的。

Go语言的标准库(通常被称为语言自带的电池),提供了清晰的构建模块和公共接口,包含I/O操作、文本处理、图像、密码学、网络和分布式应用程序等,并支持许多标准化的文件格式和编解码协议。库和工具使用了大量的约定来减少额外的配置和解释,从而最终简化程序的逻辑,而且,每个Go程序结构都是如此的相似,因此,Go程序也很容易学习。使用Go语言自带工具构建Go语言项目只需要使用文件名和标识符名称, 一个偶尔的特殊注释来确定所有的库、可执行文件、测试、基准测试、例子、以及特定于平台的变量、项目的文档等;Go语言源代码本身就包含了构建规范

学习脉络

第一章包含了本教程的基本结构,通过十几个程序介绍了用Go语言如何实现类似读写文件、文本格式化、创建图像、网络客户端和服务器通讯等日常工作。

第二章描述了Go语言程序的基本元素结构、变量、新类型定义、包和文件、以及作用域等概念。第三章讨论了数字、布尔值、字符串和常量,并演示了如何显示和处理Unicode字符。第四章描述了复合类型,从简单的数组、字典、切片到动态列表。第五章涵盖了函数,并讨论了错误处理、panic和recover,还有defer语句。

第一章到第五章是基础部分,主流命令式编程语言这部分都类似。个别之处,Go语言有自己特色的语法和风格,但是大多数程序员能很快适应。其余章节是Go语言特有的:方法、接口、并发、包、测试和反射等语言特性。

Go语言的面向对象机制与一般语言不同。它没有类层次结构,甚至可以说没有类;仅仅通过组合(而不是继承)简单的对象来构建复杂的对象。方法不仅可以定义在结构体上, 而且, 可以定义在任何用户自定义的类型上;并且, 具体类型和抽象类型(接口)之间的关系是隐式的,所以很多类型的设计者可能并不知道该类型到底实现了哪些接口。方法在第六章讨论,接口在第七章讨论。

第八章讨论了基于顺序通信进程(CSP)概念的并发编程,使用goroutines和channels处理并发编程。

第九章则讨论了传统的基于共享变量的并发编程。

第十章描述了包机制和包的组织结构。这一章还展示了如何有效地利用Go自带的工具,使用单个命令完成编译、测试、基准测试、代码格式化、文档以及其他诸多任务。

第十一章讨论了单元测试,Go语言的工具和标准库中集成了轻量级的测试功能,避免了强大但复杂的测试框架。测试库提供了一些基本构件,必要时可以用来构建复杂的测试构件。

第十二章讨论了反射,一种程序在运行期间审视自己的能力。反射是一个强大的编程工具,不过要谨慎地使用;这一章利用反射机制实现一些重要的Go语言库函数, 展示了反射的强大用法。

第十三章解释了底层编程的细节,在必要时,可以使用unsafe包绕过Go语言安全的类型系统。

环境

书中所有的代码都可以从 http://gopl.io 上的Git仓库下载。go get命令根据每个例子的导入路径智能地获取、构建并安装。只需要选择一个目录作为工作空间,然后将GOPATH环境变量设置为该路径。

语言环境

必要时,Go语言工具会创建目录。例如:

$ export GOPATH=[GOPATH]    # 选择工作目录
$ export GOROOT=[GOROOT] # Go安装的目录

在Windows中,还需要在【环境变量】中添加:

  • 【用户变量】:
    • 添加 key=GOPATH,value= <Go project directory>
    • 修改 key=Path,value中添加或修改为%GOPATH%\bin
  • 【系统变量】:
    • 添加 key=GOPATH,value=<Go project directory>
    • 添加 key=GOROOT,value=<Go install directory>

运行这些例子需要安装Go1.5以上的版本。

$ go version
go version go1.5 linux/amd64

如果使用其他的操作系统, 请参考 https://golang.org/doc/install 提供的说明安装

编程环境

这里使用 VS Code,先把 GOPATH 的目录添加到 VS Code 的工作目录。

安装 Go 插件,然后使用 Ctrl + Shift + P 快捷键来打开命令窗口,搜索 Go: Install/update Tools,然后全选安装所有工具,安装完成后会在目录下生成两个文件夹,里面有对应工具所使用到的 包(pkg) 和 可执行程序(bin)。

新建目录 src ,进入。

使用 go mod init <module name> 来生成一个模块。例如:

go mod init gopl

src 中,生成 go.mod 文件,内容如下:

module gopl

go 1.21.3

src 目录中,新建一个 main.go 文件,作为程序的入口。

可以新建目录,每个目录下所新建的 go 文件,引用为以其目录名为包名。如图,hello 目录与 main.go 文件位于同一目录。

hello_1_1.go 文件的内容如下:

package hello

import (
"fmt"
)

func SayHello() {
fmt.Println("Hello, World!")
}

可以使用以下命令进行编译或直接执行:

$ go run main.go			# 直接运行
$ go build main.go # 编译
$ .\main.exe # Windows 下执行

调试环境

在 GOPATH 下新建一个 .vscode 目录,并新建文件 launch.json ,内容如下:

{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Go debug",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}", // open file, should be main.go
"env": {
"GOPATH": "D:/coding/go",
"GOROOT": "D:/Go"
},
"args": [
// "test"
],
// "showLog": true
}
]
}

这样下断点、 F5 后,就可以调试了。

辅助文档

最佳的帮助信息来自Go语言的官方网站,https://golang.org ,它提供了完善的参考文档,包括编程语言规范和标准库等诸多权威的帮助信息。同时也包含了如何编写更地道的Go程序的基本教程,还有各种各样的在线文本资源和视频资源,它们是本书最有价值的补充。Go语言的官方博客 https://blog.golang.org 会不定期发布一些Go语言最好的实践文章,包括当前语言的发展状态、未来的计划、会议报告和Go语言相关的各种会议的主题等信息(译注: http://talks.golang.org/ 包含了官方收录的各种报告的讲稿)。

在线访问的一个有价值的地方是可以从web页面运行Go语言的程序(而纸质书则没有这么便利了)。这个功能由来自 https://play.golang.org 的 Go Playground 提供,并且可以方便地嵌入到其他页面中,例如 https://golang.org 的主页,或 godoc 提供的文档页面中。

Playground可以简单的通过执行一个小程序来测试对语法、语义和对程序库的理解,类似其他很多语言提供的REPL即时运行的工具。同时它可以生成对应的url,非常适合共享Go语言代码片段,汇报bug或提供反馈意见等。

基于 Playground 构建的 Go Tour,https://tour.golang.org ,是一个系列的Go语言入门教程,它包含了诸多基本概念和结构相关的并可在线运行的互动小程序。

当然,Playground 和 Tour 也有一些限制,它们只能导入标准库,而且因为安全的原因对一些网络库做了限制。如果要在编译和运行时需要访问互联网,对于一些更复杂的实验,你可能需要在自己的电脑上构建并运行程序。幸运的是下载Go语言的过程很简单,从 https://golang.org 下载安装包应该不超过几分钟(译注:感谢伟大的长城,让大陆的Gopher们都学会了自己打洞的基本生活技能,下载时间可能会因为洞的大小等因素从几分钟到几天或更久),然后就可以在自己电脑上编写和运行Go程序了。

Go语言是一个开源项目,你可以在 https://golang.org/pkg 阅读标准库中任意函数和类型的实现代码,和下载安装包的代码完全一致。这样,你可以知道很多函数是如何工作的, 通过挖掘找出一些答案的细节,或者仅仅是出于欣赏专业级Go代码。