Merge branch 'master' of github.com:gopl-zh/gopl-zh.github.com

This commit is contained in:
Xargin 2016-10-05 13:29:23 +08:00
commit 065317c447
33 changed files with 55 additions and 57 deletions

View File

@ -16,6 +16,7 @@ Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本
- http://shifei.me/gopl-zh/ - http://shifei.me/gopl-zh/
- http://2goo.info/media/html/gopl-zh-gh-pages/ - http://2goo.info/media/html/gopl-zh-gh-pages/
- http://docs.plhwin.com/gopl-zh/ - http://docs.plhwin.com/gopl-zh/
- http://gopl-zh.simple-is-best.tk/
**注意,在线预览不是最新版,最新以仓库里的内容为准** **注意,在线预览不是最新版,最新以仓库里的内容为准**

View File

@ -11,7 +11,7 @@
中文译者 | 章节 中文译者 | 章节
-------------------------------------- | ------------------------- -------------------------------------- | -------------------------
`chai2010 <chaishushan@gmail.com>` | 前言/第2~4章/第10~13章 `chai2010 <chaishushan@gmail.com>` | 前言/第2 ~ 4章/第10 ~ 13章
`Xargin <cao1988228@163.com>` | 第1章/第6章/第8~9章 `Xargin <cao1988228@163.com>` | 第1章/第6章/第8 ~ 9章
`CrazySssst` | 第5章 `CrazySssst` | 第5章
`foreversmart <njutree@gmail.com>` | 第7章 `foreversmart <njutree@gmail.com>` | 第7章

View File

@ -1,4 +1,4 @@
# 第一章 入门 # 第一章 入门
本章介绍Go语言的基础组件。本章提供了足够的信息和示例程序希望可以帮你尽快入门, 写出有用的程序。本章和之后章节的示例程序都针对你可能遇到的现实案例。先了解几个Go程序涉及的主题从简单的文件处理、图像处理到互联网客户端和服务端并发。当然第一章不会解释细枝末节但用这些程序来学习一门新语言还是很有效的。 本章介绍Go语言的基础组件。本章提供了足够的信息和示例程序希望可以帮你尽快入门, 写出有用的程序。本章和之后章节的示例程序都针对你可能遇到的现实案例。先了解几个Go程序涉及的主题从简单的文件处理、图像处理到互联网客户端和服务端并发。当然第一章不会解释细枝末节但用这些程序来学习一门新语言还是很有效的。

View File

@ -1,4 +1,4 @@
# 第十章 包和工具 # 第十章 包和工具
现在随便一个小程序的实现都可能包含超过10000个函数。然而作者一般只需要考虑其中很小的一部分和做很少的设计因为绝大部分代码都是由他人编写的它们通过类似包或模块的方式被重用。 现在随便一个小程序的实现都可能包含超过10000个函数。然而作者一般只需要考虑其中很小的一部分和做很少的设计因为绝大部分代码都是由他人编写的它们通过类似包或模块的方式被重用。

View File

@ -1,4 +1,4 @@
# 第十一章 测试 # 第十一章 测试
Maurice Wilkes第一个存储程序计算机EDSAC的设计者1949年他在实验室爬楼梯时有一个顿悟。在《计算机先驱回忆录》Memoirs of a Computer Pioneer他回忆到“忽然间有一种醍醐灌顶的感觉我整个后半生的美好时光都将在寻找程序BUG中度过了”。肯定从那之后的大部分正常的码农都会同情Wilkes过分悲观的想法虽然也许会有人困惑于他对软件开发的难度的天真看法。 Maurice Wilkes第一个存储程序计算机EDSAC的设计者1949年他在实验室爬楼梯时有一个顿悟。在《计算机先驱回忆录》Memoirs of a Computer Pioneer他回忆到“忽然间有一种醍醐灌顶的感觉我整个后半生的美好时光都将在寻找程序BUG中度过了”。肯定从那之后的大部分正常的码农都会同情Wilkes过分悲观的想法虽然也许会有人困惑于他对软件开发的难度的天真看法。

View File

@ -1,4 +1,4 @@
# 第十二章 反射 # 第十二章 反射
Go语言提供了一种机制能够在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作而不需要在编译时就知道这些变量的具体类型。这种机制被称为反射。反射也可以让我们将类型本身作为第一类的值类型处理。 Go语言提供了一种机制能够在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作而不需要在编译时就知道这些变量的具体类型。这种机制被称为反射。反射也可以让我们将类型本身作为第一类的值类型处理。

View File

@ -15,17 +15,17 @@ Sizeof函数返回的大小只包括数据结构中固定的部分例如字
类型 | 大小 类型 | 大小
----------------------------- | ---- ------------------------------- | -----------------------------
bool | 1个字节 `bool` | 1个字节
intN, uintN, floatN, complexN | N/8个字节(例如float64是8个字节) `intN, uintN, floatN, complexN` | N/8个字节(例如float64是8个字节)
int, uint, uintptr | 1个机器字 `int, uint, uintptr` | 1个机器字
*T | 1个机器字 `*T` | 1个机器字
string | 2个机器字(data,len) `string` | 2个机器字(data,len)
[]T | 3个机器字(data,len,cap) `[]T` | 3个机器字(data,len,cap)
map | 1个机器字 `map` | 1个机器字
func | 1个机器字 `func` | 1个机器字
chan | 1个机器字 `chan` | 1个机器字
interface | 2个机器字(type,value) `interface` | 2个机器字(type,value)
Go语言的规范并没有要求一个字段的声明顺序和内存中的顺序是一致的所以理论上一个编译器可以随意地重新排列每个字段的内存位置虽然在写作本书的时候编译器还没有这么做。下面的三个结构体虽然有着相同的字段但是第一种写法比另外的两个需要多50%的内存。 Go语言的规范并没有要求一个字段的声明顺序和内存中的顺序是一致的所以理论上一个编译器可以随意地重新排列每个字段的内存位置虽然在写作本书的时候编译器还没有这么做。下面的三个结构体虽然有着相同的字段但是第一种写法比另外的两个需要多50%的内存。

View File

@ -1,4 +1,4 @@
# 第13章 底层编程 # 第十三章 底层编程
Go语言的设计包含了诸多安全策略限制了可能导致程序运行出错的用法。编译时类型检查可以发现大多数类型不匹配的操作例如两个字符串做减法的错误。字符串、map、slice和chan等所有的内置类型都有严格的类型转换规则。 Go语言的设计包含了诸多安全策略限制了可能导致程序运行出错的用法。编译时类型检查可以发现大多数类型不匹配的操作例如两个字符串做减法的错误。字符串、map、slice和chan等所有的内置类型都有严格的类型转换规则。

View File

@ -1,4 +1,4 @@
# 第二章 程序结构 # 第二章 程序结构
Go语言和其他编程语言一样一个大的程序是由很多小的基础构件组成的。变量保存值简单的加法和减法运算被组合成较复杂的表达式。基础类型被聚合为数组或结构体等更复杂的数据结构。然后使用if和for之类的控制语句来组织和控制表达式的执行流程。然后多个语句被组织到一个个函数中以便代码的隔离和复用。函数以源文件和包的方式被组织。 Go语言和其他编程语言一样一个大的程序是由很多小的基础构件组成的。变量保存值简单的加法和减法运算被组合成较复杂的表达式。基础类型被聚合为数组或结构体等更复杂的数据结构。然后使用if和for之类的控制语句来组织和控制表达式的执行流程。然后多个语句被组织到一个个函数中以便代码的隔离和复用。函数以源文件和包的方式被组织。

View File

@ -12,7 +12,7 @@ Unicode字符rune类型是和int32等价的类型通常用于表示一个Unic
不管它们的具体大小int、uint和uintptr是不同类型的兄弟类型。其中int和int32也是不同的类型即使int的大小也是32bit在需要将int当作int32类型的地方需要一个显式的类型转换操作反之亦然。 不管它们的具体大小int、uint和uintptr是不同类型的兄弟类型。其中int和int32也是不同的类型即使int的大小也是32bit在需要将int当作int32类型的地方需要一个显式的类型转换操作反之亦然。
其中有符号整数采用2的补码形式表示也就是最高bit位用来表示符号位一个n-bit的有符号数的值域是从$$-2^{n-1}$$$$2^{n-1}-1$$。无符号整数的所有bit位都用于表示非负数值域是0到$$2^n-1$$。例如int8类型整数的值域是从-128到127而uint8类型整数的值域是从0到255。 其中有符号整数采用2的补码形式表示也就是最高bit位用来表示符号位一个n-bit的有符号数的值域是从$-2^{n-1}$到$2^{n-1}-1$。无符号整数的所有bit位都用于表示非负数值域是0到$2^n-1$。例如int8类型整数的值域是从-128到127而uint8类型整数的值域是从0到255。
下面是Go语言中关于算术运算、逻辑运算和比较运算的二元运算符它们按照先级递减的顺序的排列 下面是Go语言中关于算术运算、逻辑运算和比较运算的二元运算符它们按照先级递减的顺序的排列
@ -101,7 +101,7 @@ fmt.Printf("%08b\n", x>>1) // "00010001", the set {0, 4}
6.5节给出了一个可以远大于一个字节的整数集的实现。) 6.5节给出了一个可以远大于一个字节的整数集的实现。)
在`x<<n``x>>n`移位运算中决定了移位操作bit数部分必须是无符号数被操作的x数可以是有符号或无符号数。算术上一个`x<<n`左移运算等价于乘以$$2^n$$一个`x>>n`右移运算等价于除以$$2^n$$。 在`x<<n``x>>n`移位运算中决定了移位操作bit数部分必须是无符号数被操作的x数可以是有符号或无符号数。算术上一个`x<<n`左移运算等价于乘以$2^n$一个`x>>n`右移运算等价于除以$2^n$。
左移运算用零填充右边空缺的bit位无符号数的右移运算也是用0填充左边空缺的bit位但是有符号数的右移运算会用符号位的值填充左边空缺的bit位。因为这个原因最好用无符号运算这样你可以将整数完全当作一个bit位模式处理。 左移运算用零填充右边空缺的bit位无符号数的右移运算也是用0填充左边空缺的bit位但是有符号数的右移运算会用符号位的值填充左边空缺的bit位。因为这个原因最好用无符号运算这样你可以将整数完全当作一个bit位模式处理。
@ -114,7 +114,7 @@ for i := len(medals) - 1; i >= 0; i-- {
} }
``` ```
另一个选择对于上面的例子来说将是灾难性的。如果len函数返回一个无符号数那么i也将是无符号的uint类型然后条件`i >= 0`则永远为真。在三次迭代之后,也就是`i == 0`时i--语句将不会产生-1而是变成一个uint类型的最大值可能是$$2^64-1$$然后medals[i]表达式将发生运行时panic异常§5.9也就是试图访问一个slice范围以外的元素。 另一个选择对于上面的例子来说将是灾难性的。如果len函数返回一个无符号数那么i也将是无符号的uint类型然后条件`i >= 0`则永远为真。在三次迭代之后,也就是`i == 0`时i--语句将不会产生-1而是变成一个uint类型的最大值可能是$2^64-1$然后medals[i]表达式将发生运行时panic异常§5.9也就是试图访问一个slice范围以外的元素。
出于这个原因无符号数往往只有在位运算或其它特殊的运算场景才会使用就像bit集合、分析二进制文件格式或者是哈希和加密操作等。它们通常并不用于仅仅是表达非负数量的场合。 出于这个原因无符号数往往只有在位运算或其它特殊的运算场景才会使用就像bit集合、分析二进制文件格式或者是哈希和加密操作等。它们通常并不用于仅仅是表达非负数量的场合。

View File

@ -89,7 +89,7 @@ func mandelbrot(z complex128) color.Color {
**练习 3.6** 升采样技术可以降低每个像素对计算颜色值和平均值的影响。简单的方法是将每个像素分成四个子像素,实现它。 **练习 3.6** 升采样技术可以降低每个像素对计算颜色值和平均值的影响。简单的方法是将每个像素分成四个子像素,实现它。
**练习 3.7** 另一个生成分形图像的方式是使用牛顿法来求解一个复数方程,例如$$z^4-1=0$$。每个起点到四个根的迭代次数对应阴影的灰度。方程根对应的点用颜色表示。 **练习 3.7** 另一个生成分形图像的方式是使用牛顿法来求解一个复数方程,例如$z^4-1=0$。每个起点到四个根的迭代次数对应阴影的灰度。方程根对应的点用颜色表示。
**练习 3.8** 通过提高精度来生成更多级别的分形。使用四种不同精度类型的数字实现相同的分形complex64、complex128、big.Float和big.Rat。后面两种类型在math/big包声明。Float是有指定限精度的浮点数Rat是无限精度的有理数。它们间的性能和内存使用对比如何当渲染图可见时缩放的级别是多少 **练习 3.8** 通过提高精度来生成更多级别的分形。使用四种不同精度类型的数字实现相同的分形complex64、complex128、big.Float和big.Rat。后面两种类型在math/big包声明。Float是有指定限精度的浮点数Rat是无限精度的有理数。它们间的性能和内存使用对比如何当渲染图可见时缩放的级别是多少

View File

@ -25,7 +25,7 @@
\\ 反斜杠 \\ 反斜杠
``` ```
可以通过十六进制或八进制转义在字符串面值包含任意的字节。一个十六进制的转义形式是\xhh其中两个h表示十六进制数字大写或小写都可以。一个八进制转义形式是\ooo包含三个八进制的o数字0到7但是不能超过`\377`译注对应一个字节的范围十进制为255。每一个单一的字节表达一个特定的值。稍后我们将看到如何将一个Unicode码点写到字符串面值中。 可以通过十六进制或八进制转义在字符串面值包含任意的字节。一个十六进制的转义形式是`\xhh`其中两个h表示十六进制数字大写或小写都可以。一个八进制转义形式是`\ooo`包含三个八进制的o数字0到7但是不能超过`\377`译注对应一个字节的范围十进制为255。每一个单一的字节表达一个特定的值。稍后我们将看到如何将一个Unicode码点写到字符串面值中。
一个原生的字符串面值形式是\`...\`,使用反引号代替双引号。在原生的字符串面值中,没有转义操作;全部的内容都是字面的意思,包含退格和换行,因此一个程序中的原生字符串面值可能跨越多行(译注:在原生字符串面值内部是无法直接写\`字符的,可以用八进制或十六进制转义或+"\`"链接字符串常量完成。唯一的特殊处理是会删除回车以保证在所有平台上的值都是一样的包括那些把回车也放入文本文件的系统译注Windows系统会把回车和换行一起放入文本文件中 一个原生的字符串面值形式是\`...\`,使用反引号代替双引号。在原生的字符串面值中,没有转义操作;全部的内容都是字面的意思,包含退格和换行,因此一个程序中的原生字符串面值可能跨越多行(译注:在原生字符串面值内部是无法直接写\`字符的,可以用八进制或十六进制转义或+"\`"链接字符串常量完成。唯一的特殊处理是会删除回车以保证在所有平台上的值都是一样的包括那些把回车也放入文本文件的系统译注Windows系统会把回车和换行一起放入文本文件中

View File

@ -13,7 +13,7 @@ UTF8是一个将Unicode码点编码为字节序列的变长编码。UTF8编码
Go语言的源文件采用UTF8编码并且Go语言处理UTF8编码的文本也很出色。unicode包提供了诸多处理rune字符相关功能的函数比如区分字母和数组或者是字母的大写和小写转换等unicode/utf8包则提供了用于rune字符序列的UTF8编码和解码的功能。 Go语言的源文件采用UTF8编码并且Go语言处理UTF8编码的文本也很出色。unicode包提供了诸多处理rune字符相关功能的函数比如区分字母和数组或者是字母的大写和小写转换等unicode/utf8包则提供了用于rune字符序列的UTF8编码和解码的功能。
有很多Unicode字符很难直接从键盘输入并且还有很多字符有着相似的结构有一些甚至是不可见的字符译注中文和日文就有很多相似但不同的字。Go语言字符串面值中的Unicode转义字符让我们可以通过Unicode码点输入特殊的字符。有两种形式\uhhhh对应16bit的码点值\Uhhhhhhhh对应32bit的码点值其中h是一个十六进制数字一般很少需要使用32bit的形式。每一个对应码点的UTF8编码。例如下面的字母串面值都表示相同的值 有很多Unicode字符很难直接从键盘输入并且还有很多字符有着相似的结构有一些甚至是不可见的字符译注中文和日文就有很多相似但不同的字。Go语言字符串面值中的Unicode转义字符让我们可以通过Unicode码点输入特殊的字符。有两种形式`\uhhhh`对应16bit的码点值`\Uhhhhhhhh`对应32bit的码点值其中h是一个十六进制数字一般很少需要使用32bit的形式。每一个对应码点的UTF8编码。例如下面的字母串面值都表示相同的值
``` ```
"世界" "世界"
@ -30,7 +30,7 @@ Unicode转义也可以使用在rune字符中。下面三个字符是等价的
'世' '\u4e16' '\U00004e16' '世' '\u4e16' '\U00004e16'
``` ```
对于小于256码点值可以写在一个十六进制转义字节中例如'\x41'对应字符'A',但是对于更大的码点则必须使用\u或\U转义形式。因此'\xe4\xb8\x96'并不是一个合法的rune字符虽然这三个字节对应一个有效的UTF8编码的码点。 对于小于256码点值可以写在一个十六进制转义字节中例如`\x41`对应字符'A',但是对于更大的码点则必须使用`\u`或`\U`转义形式。因此,`\xe4\xb8\x96`并不是一个合法的rune字符虽然这三个字节对应一个有效的UTF8编码的码点。
得益于UTF8编码优良的设计诸多字符串操作都不需要解码操作。我们可以不用解码直接测试一个字符串是否是另一个字符串的前缀 得益于UTF8编码优良的设计诸多字符串操作都不需要解码操作。我们可以不用解码直接测试一个字符串是否是另一个字符串的前缀
@ -115,7 +115,7 @@ for range s {
正如我们前面提到的文本字符串采用UTF8编码只是一种惯例但是对于循环的真正字符串并不是一个惯例这是正确的。如果用于循环的字符串只是一个普通的二进制数据或者是含有错误编码的UTF8数据将会发送什么呢 正如我们前面提到的文本字符串采用UTF8编码只是一种惯例但是对于循环的真正字符串并不是一个惯例这是正确的。如果用于循环的字符串只是一个普通的二进制数据或者是含有错误编码的UTF8数据将会发送什么呢
每一个UTF8字符解码不管是显式地调用utf8.DecodeRuneInString解码或是在range循环中隐式地解码如果遇到一个错误的UTF8编码输入将生成一个特别的Unicode字符'\uFFFD',在印刷中这个符号通常是一个黑色六角或钻石形状,里面包含一个白色的问号"<22>"。当程序遇到这样的一个字符通常是一个危险信号说明输入并不是一个完美没有错误的UTF8字符串。 每一个UTF8字符解码不管是显式地调用utf8.DecodeRuneInString解码或是在range循环中隐式地解码如果遇到一个错误的UTF8编码输入将生成一个特别的Unicode字符`\uFFFD`,在印刷中这个符号通常是一个黑色六角或钻石形状,里面包含一个白色的问号"<22>"。当程序遇到这样的一个字符通常是一个危险信号说明输入并不是一个完美没有错误的UTF8字符串。
UTF8字符串作为交换格式是非常方便的但是在程序内部采用rune序列可能更方便因为rune大小一致支持数组索引和方便切割。 UTF8字符串作为交换格式是非常方便的但是在程序内部采用rune序列可能更方便因为rune大小一致支持数组索引和方便切割。
@ -144,7 +144,7 @@ fmt.Println(string(65)) // "A", not "65"
fmt.Println(string(0x4eac)) // "京" fmt.Println(string(0x4eac)) // "京"
``` ```
如果对应码点的字符是无效的,则用'\uFFFD'无效字符作为替换: 如果对应码点的字符是无效的,则用`\uFFFD`无效字符作为替换:
```Go ```Go
fmt.Println(string(1234567)) // "<22>" fmt.Println(string(1234567)) // "<22>"

View File

@ -55,7 +55,7 @@ func basename(s string) string {
} }
``` ```
path和path/filepath包提供了关于文件路径名更一般的函数操作。使用斜杠分隔路径可以在任何操作系统上工作。斜杠本身不应该用于文件名但是在其他一些领域可能会用于文件名例如URL路径组件。相比之下path/filepath包则使用操作系统本身的路径规则例如POSIX系统使用/foo/bar而Microsoft Windows使用c:\foo\bar等。 path和path/filepath包提供了关于文件路径名更一般的函数操作。使用斜杠分隔路径可以在任何操作系统上工作。斜杠本身不应该用于文件名但是在其他一些领域可能会用于文件名例如URL路径组件。相比之下path/filepath包则使用操作系统本身的路径规则例如POSIX系统使用/foo/bar而Microsoft Windows使用`c:\foo\bar`等。
让我们继续另一个字符串的例子。函数的功能是将一个表示整值的字符串每隔三个字符插入一个逗号分隔符例如“12345”处理后成为“12,345”。这个版本只适用于整数类型支持浮点数类型的支持留作练习。 让我们继续另一个字符串的例子。函数的功能是将一个表示整值的字符串每隔三个字符插入一个逗号分隔符例如“12345”处理后成为“12,345”。这个版本只适用于整数类型支持浮点数类型的支持留作练习。

View File

@ -26,7 +26,7 @@ var y float64 = Pi64
var z complex128 = complex128(Pi64) var z complex128 = complex128(Pi64)
``` ```
对于常量面值不同的写法可能会对应不同的类型。例如0、0.0、0i和'\u0000'虽然有着相同的常量值但是它们分别对应无类型的整数、无类型的浮点数、无类型的复数和无类型的字符等不同的常量类型。同样true和false也是无类型的布尔类型字符串面值常量是无类型的字符串类型。 对于常量面值不同的写法可能会对应不同的类型。例如0、0.0、0i和`\u0000`虽然有着相同的常量值但是它们分别对应无类型的整数、无类型的浮点数、无类型的复数和无类型的字符等不同的常量类型。同样true和false也是无类型的布尔类型字符串面值常量是无类型的字符串类型。
前面说过除法运算符/会根据操作数的类型生成对应类型的结果。因此,不同写法的常量除法表达式可能对应不同的结果: 前面说过除法运算符/会根据操作数的类型生成对应类型的结果。因此,不同写法的常量除法表达式可能对应不同的结果:

View File

@ -1,4 +1,4 @@
# 第三章 基础数据类型 # 第三章 基础数据类型
虽然从底层而言所有的数据都是由比特组成但计算机一般操作的是固定大小的数如整数、浮点数、比特数组、内存地址等。进一步将这些数组织在一起就可表达更多的对象例如数据包、像素点、诗歌甚至其他任何对象。Go语言提供了丰富的数据组织形式这依赖于Go语言内置的数据类型。这些内置的数据类型兼顾了硬件的特性和表达复杂数据结构的便捷性。 虽然从底层而言所有的数据都是由比特组成但计算机一般操作的是固定大小的数如整数、浮点数、比特数组、内存地址等。进一步将这些数组织在一起就可表达更多的对象例如数据包、像素点、诗歌甚至其他任何对象。Go语言提供了丰富的数据组织形式这依赖于Go语言内置的数据类型。这些内置的数据类型兼顾了硬件的特性和表达复杂数据结构的便捷性。

View File

@ -6,7 +6,7 @@ Go语言对于这些标准格式的编码和解码都有良好的支持由标
JSON是对JavaScript中各种类型的值——字符串、数字、布尔值和对象——Unicode本文编码。它可以用有效可读的方式表示第三章的基础数据类型和本章的数组、slice、结构体和map等聚合数据类型。 JSON是对JavaScript中各种类型的值——字符串、数字、布尔值和对象——Unicode本文编码。它可以用有效可读的方式表示第三章的基础数据类型和本章的数组、slice、结构体和map等聚合数据类型。
基本的JSON类型有数字十进制或科学记数法、布尔值true或false、字符串其中字符串是以双引号包含的Unicode字符序列支持和Go语言类似的反斜杠转义特性不过JSON使用的是\Uhhhh转义数字来表示一个UTF-16编码译注UTF-16和UTF-8一样是一种变长的编码有些Unicode码点较大的字符需要用4个字节表示而且UTF-16还有大端和小端的问题而不是Go语言的rune类型。 基本的JSON类型有数字十进制或科学记数法、布尔值true或false、字符串其中字符串是以双引号包含的Unicode字符序列支持和Go语言类似的反斜杠转义特性不过JSON使用的是`\Uhhhh`转义数字来表示一个UTF-16编码译注UTF-16和UTF-8一样是一种变长的编码有些Unicode码点较大的字符需要用4个字节表示而且UTF-16还有大端和小端的问题而不是Go语言的rune类型。
这些基础类型可以通过JSON的数组和对象类型进行递归组合。一个JSON数组是一个有序的值序列写在一个方括号中并以逗号分隔一个JSON数组可以用于编码Go语言的数组和slice。一个JSON对象是一个字符串到值的映射写成以系列的name:value对形式用花括号包含并以逗号分隔JSON的对象类型可以用于编码Go语言的map类型key类型是字符串和结构体。例如 这些基础类型可以通过JSON的数组和对象类型进行递归组合。一个JSON数组是一个有序的值序列写在一个方括号中并以逗号分隔一个JSON数组可以用于编码Go语言的数组和slice。一个JSON对象是一个字符串到值的映射写成以系列的name:value对形式用花括号包含并以逗号分隔JSON的对象类型可以用于编码Go语言的map类型key类型是字符串和结构体。例如

View File

@ -1,4 +1,4 @@
# 第四章 复合数据类型 # 第四章 复合数据类型
在第三章我们讨论了基本数据类型它们可以用于构建程序中数据结构是Go语言的世界的原子。在本章我们将讨论复合数据类型它是以不同的方式组合基本类型可以构造出来的复合数据类型。我们主要讨论四种类型——数组、slice、map和结构体——同时在本章的最后我们将演示如何使用结构体来解码和编码到对应JSON格式的数据并且通过结合使用模板来生成HTML页面。 在第三章我们讨论了基本数据类型它们可以用于构建程序中数据结构是Go语言的世界的原子。在本章我们将讨论复合数据类型它是以不同的方式组合基本类型可以构造出来的复合数据类型。我们主要讨论四种类型——数组、slice、map和结构体——同时在本章的最后我们将演示如何使用结构体来解码和编码到对应JSON格式的数据并且通过结合使用模板来生成HTML页面。

View File

@ -2,7 +2,7 @@
拥有函数名的函数只能在包级语法块中被声明通过函数字面量function literal我们可绕过这一限制在任何表达式中表示一个函数值。函数字面量的语法和函数声明相似区别在于func关键字后没有函数名。函数值字面量是一种表达式它的值被成为匿名函数anonymous function 拥有函数名的函数只能在包级语法块中被声明通过函数字面量function literal我们可绕过这一限制在任何表达式中表示一个函数值。函数字面量的语法和函数声明相似区别在于func关键字后没有函数名。函数值字面量是一种表达式它的值被成为匿名函数anonymous function
函数字面量允许我们在使用函数时再定义它。通过这种技巧我们可以改写之前对strings.Map的调用 函数字面量允许我们在使用函数时再定义它。通过这种技巧我们可以改写之前对strings.Map的调用
```Go ```Go
strings.Map(func(r rune) rune { return r + 1 }, "HAL-9000") strings.Map(func(r rune) rune { return r + 1 }, "HAL-9000")

View File

@ -1,4 +1,4 @@
# 第五章 函数 # 第五章 函数
函数可以让我们将一个语句序列打包为一个单元,然后可以从程序中其它地方多次调用。函数的机制可以让我们将一个大的工作分解为小的任务,这样的小任务可以让不同程序员在不同时间、不同地方独立完成。一个函数同时对用户隐藏了其实现细节。由于这些因素,对于任何编程语言来说,函数都是一个至关重要的部分。 函数可以让我们将一个语句序列打包为一个单元,然后可以从程序中其它地方多次调用。函数的机制可以让我们将一个大的工作分解为小的任务,这样的小任务可以让不同程序员在不同时间、不同地方独立完成。一个函数同时对用户隐藏了其实现细节。由于这些因素,对于任何编程语言来说,函数都是一个至关重要的部分。

View File

@ -1,4 +1,4 @@
# 第六章 方法 # 第六章 方法
从90年代早期开始面向对象编程(OOP)就成为了称霸工程界和教育界的编程范式所以之后几乎所有大规模被应用的语言都包含了对OOP的支持go语言也不例外。 从90年代早期开始面向对象编程(OOP)就成为了称霸工程界和教育界的编程范式所以之后几乎所有大规模被应用的语言都包含了对OOP的支持go语言也不例外。

View File

@ -1,4 +1,4 @@
# 第七章 接口 # 第七章 接口
接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力。 接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力。

View File

@ -8,9 +8,9 @@ func squarer(out, in chan int)
func printer(in chan int) func printer(in chan int)
``` ```
其中squarer计算平方的函数在两个串联Channels的中间因此拥有两个channels类型的参数一个用于输入一个用于输出。每个channels都用有相同的类型但是它们的使用方式一个只用于接收另一个只用于发送。参数的名字in和out已经明确表示了这个意图但是并无法保证squarer函数向一个in参数对应的channels发送数据或者从一个out参数对应的channels接收数据。 其中squarer计算平方的函数在两个串联Channels的中间因此拥有两个channels类型的参数一个用于输入一个用于输出。每个channels都用有相同的类型但是它们的使用方式一个只用于接收另一个只用于发送。参数的名字in和out已经明确表示了这个意图但是并无法保证squarer函数向一个in参数对应的channels发送数据或者从一个out参数对应的channels接收数据。
这种场景是典型的。当一个channel作为一个函数参数,它一般总是被专门用于只发送或者只接收。 这种场景是典型的。当一个channel作为一个函数参数,它一般总是被专门用于只发送或者只接收。
为了表明这种意图并防止被滥用Go语言的类型系统提供了单方向的channel类型分别用于只发送或只接收的channel。类型`chan<- int`表示一个只发送int的channel只能发送不能接收相反类型`<-chan int`表示一个只接收int的channel只能接收不能发送箭头`<-`和关键字chan的相对位置表明了channel的方向这种限制将在编译期检测 为了表明这种意图并防止被滥用Go语言的类型系统提供了单方向的channel类型分别用于只发送或只接收的channel。类型`chan<- int`表示一个只发送int的channel只能发送不能接收相反类型`<-chan int`表示一个只接收int的channel只能接收不能发送箭头`<-`和关键字chan的相对位置表明了channel的方向这种限制将在编译期检测

View File

@ -51,7 +51,7 @@ fmt.Println(<-ch) // "B"
fmt.Println(<-ch) // "C" fmt.Println(<-ch) // "C"
``` ```
在这个例子中发送和接收操作都发生在同一个goroutine中但是在真的程序中它们一般由不同的goroutine执行。Go语言新手有时候会将一个带缓存的channel当作同一个goroutine中的队列使用虽然语法看似简单但实际上这是一个错误。Channel和goroutine的调度器机制是紧密相连的一个发送操作——或许是整个程序——可能会永远阻塞。如果你只是需要一个简单的队列使用slice就可以了。 在这个例子中发送和接收操作都发生在同一个goroutine中但是在真的程序中它们一般由不同的goroutine执行。Go语言新手有时候会将一个带缓存的channel当作同一个goroutine中的队列使用虽然语法看似简单但实际上这是一个错误。Channel和goroutine的调度器机制是紧密相连的一个发送操作——或许是整个程序——可能会永远阻塞。如果你只是需要一个简单的队列使用slice就可以了。
下面的例子展示了一个使用了带缓存channel的应用。它并发地向三个镜像站点发出请求三个镜像站点分散在不同的地理位置。它们分别将收到的响应发送到带缓存channel最后接收者只接收第一个收到的响应也就是最快的那个响应。因此mirroredQuery函数可能在另外两个响应慢的镜像站点响应之前就返回了结果。顺便说一下多个goroutines并发地向同一个channel发送数据或从同一个channel接收数据都是常见的用法。 下面的例子展示了一个使用了带缓存channel的应用。它并发地向三个镜像站点发出请求三个镜像站点分散在不同的地理位置。它们分别将收到的响应发送到带缓存channel最后接收者只接收第一个收到的响应也就是最快的那个响应。因此mirroredQuery函数可能在另外两个响应慢的镜像站点响应之前就返回了结果。顺便说一下多个goroutines并发地向同一个channel发送数据或从同一个channel接收数据都是常见的用法。
@ -73,7 +73,7 @@ func request(hostname string) (response string) { /* ... */ }
Channel的缓存也可能影响程序的性能。想象一家蛋糕店有三个厨师一个烘焙一个上糖衣还有一个将每个蛋糕传递到它下一个厨师在生产线。在狭小的厨房空间环境每个厨师在完成蛋糕后必须等待下一个厨师已经准备好接受它这类似于在一个无缓存的channel上进行沟通。 Channel的缓存也可能影响程序的性能。想象一家蛋糕店有三个厨师一个烘焙一个上糖衣还有一个将每个蛋糕传递到它下一个厨师在生产线。在狭小的厨房空间环境每个厨师在完成蛋糕后必须等待下一个厨师已经准备好接受它这类似于在一个无缓存的channel上进行沟通。
如果在每个厨师之间有一个放置一个蛋糕的额外空间那么每个厨师就可以将一个完成的蛋糕临时放在那里而马上进入下一个蛋糕在制作中这类似于将channel的缓存队列的容量设置为1。只要每个厨师的平均工作效率相近那么其中大部分的传输工作将是迅速的个体之间细小的效率差异将在交接过程中弥补。如果厨师之间有更大的额外空间——也是就更大容量的缓存队列——将可以在不停止生产线的前提下消除更大的效率波动例如一个厨师可以短暂地休息然后加快赶上进度而不影响其他人。 如果在每个厨师之间有一个放置一个蛋糕的额外空间那么每个厨师就可以将一个完成的蛋糕临时放在那里而马上进入下一个蛋糕在制作中这类似于将channel的缓存队列的容量设置为1。只要每个厨师的平均工作效率相近那么其中大部分的传输工作将是迅速的个体之间细小的效率差异将在交接过程中弥补。如果厨师之间有更大的额外空间——也是就更大容量的缓存队列——将可以在不停止生产线的前提下消除更大的效率波动例如一个厨师可以短暂地休息然后加快赶上进度而不影响其他人。
另一方面,如果生产线的前期阶段一直快于后续阶段,那么它们之间的缓存在大部分时间都将是满的。相反,如果后续阶段比前期阶段更快,那么它们之间的缓存在大部分时间都将是空的。对于这类场景,额外的缓存并没有带来任何好处。 另一方面,如果生产线的前期阶段一直快于后续阶段,那么它们之间的缓存在大部分时间都将是满的。相反,如果后续阶段比前期阶段更快,那么它们之间的缓存在大部分时间都将是空的。对于这类场景,额外的缓存并没有带来任何好处。

View File

@ -8,9 +8,9 @@
ch := make(chan int) // ch has type 'chan int' ch := make(chan int) // ch has type 'chan int'
``` ```
和map类似channel也一个对应make创建的底层数据结构的引用。当我们复制一个channel或用于函数参数传递时我们只是拷贝了一个channel引用因此调用者被调用者将引用同一个channel对象。和其它的引用类型一样channel的零值也是nil。 和map类似channel也一个对应make创建的底层数据结构的引用。当我们复制一个channel或用于函数参数传递时我们只是拷贝了一个channel引用因此调用者被调用者将引用同一个channel对象。和其它的引用类型一样channel的零值也是nil。
两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相的对象那么比较的结果为真。一个channel也可以和nil进行比较。 两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相的对象那么比较的结果为真。一个channel也可以和nil进行比较。
一个channel有发送和接受两个主要操作都是通信行为。一个发送语句将一个值从一个goroutine通过channel发送到另一个执行接收操作的goroutine。发送和接收两个操作都是用`<-`运算符在发送语句中`<-`运算符分割channel和要发送的值在接收语句中`<-`运算符写在channel对象之前一个不使用接收结果的接收操作也是合法的 一个channel有发送和接受两个主要操作都是通信行为。一个发送语句将一个值从一个goroutine通过channel发送到另一个执行接收操作的goroutine。发送和接收两个操作都是用`<-`运算符在发送语句中`<-`运算符分割channel和要发送的值在接收语句中`<-`运算符写在channel对象之前一个不使用接收结果的接收操作也是合法的
@ -20,7 +20,7 @@ x = <-ch // a receive expression in an assignment statement
<-ch // a receive statement; result is discarded <-ch // a receive statement; result is discarded
``` ```
Channel还支持close操作用于关闭channel随后对基于该channel的任何发送操作都将导致panic异常。对一个已经被close过的channel行接收操作依然可以接受到之前已经成功发送的数据如果channel中已经没有数据的话讲产生一个零值的数据。 Channel还支持close操作用于关闭channel随后对基于该channel的任何发送操作都将导致panic异常。对一个已经被close过的channel行接收操作依然可以接受到之前已经成功发送的数据如果channel中已经没有数据的话讲产生一个零值的数据。
使用内置的close函数就可以关闭一个channel 使用内置的close函数就可以关闭一个channel

View File

@ -98,7 +98,7 @@ func makeThumbnails4(filenames []string) error {
} }
``` ```
这个程序有一个微的bug。当它遇到第一个非nil的error时会直接将error返回到调用方使得没有一个goroutine去排空errors channel。这样剩下的worker goroutine在向这个channel中发送值时都会永远地阻塞下去并且永远都不会退出。这种情况叫做goroutine泄露(§8.4.4)可能会导致整个程序卡住或者跑出out of memory的错误。 这个程序有一个微的bug。当它遇到第一个非nil的error时会直接将error返回到调用方使得没有一个goroutine去排空errors channel。这样剩下的worker goroutine在向这个channel中发送值时都会永远地阻塞下去并且永远都不会退出。这种情况叫做goroutine泄露(§8.4.4)可能会导致整个程序卡住或者跑出out of memory的错误。
最简单的解决办法就是用一个具有合适大小的buffered channel这样这些worker goroutine向channel中发送错误时就不会被阻塞。(一个可选的解决办法是创建一个另外的goroutine当main goroutine返回第一个错误的同时去排空channel) 最简单的解决办法就是用一个具有合适大小的buffered channel这样这些worker goroutine向channel中发送错误时就不会被阻塞。(一个可选的解决办法是创建一个另外的goroutine当main goroutine返回第一个错误的同时去排空channel)

View File

@ -64,7 +64,7 @@ func main() {
``` ```
下面这个例子更微。ch这个channel的buffer大小是1所以会交替的为空或为满所以只有一个case可以进行下去无论i是奇数或者偶数它都会打印0 2 4 6 8。 下面这个例子更微。ch这个channel的buffer大小是1所以会交替的为空或为满所以只有一个case可以进行下去无论i是奇数或者偶数它都会打印0 2 4 6 8。
```go ```go
ch := make(chan int, 1) ch := make(chan int, 1)

View File

@ -1,4 +1,4 @@
# 第八章 Goroutines和Channels # 第八章 Goroutines和Channels
并发程序指同时进行多个任务的程序随着硬件的发展并发程序变得越来越重要。Web服务器会一次处理成千上万的请求。平板电脑和手机app在渲染用户画面同时还会后台执行各种计算任务和网络请求。即使是传统的批处理问题--读取数据,计算,写输出--现在也会用并发来隐藏掉I/O的操作延迟以充分利用现代计算机设备的多个核心。计算机的性能每年都在以非线性的速度增长。 并发程序指同时进行多个任务的程序随着硬件的发展并发程序变得越来越重要。Web服务器会一次处理成千上万的请求。平板电脑和手机app在渲染用户画面同时还会后台执行各种计算任务和网络请求。即使是传统的批处理问题--读取数据,计算,写输出--现在也会用并发来隐藏掉I/O的操作延迟以充分利用现代计算机设备的多个核心。计算机的性能每年都在以非线性的速度增长。

View File

@ -8,4 +8,4 @@
竞争检查器会报告所有的已经发生的数据竞争。然而,它只能检测到运行时的竞争条件;并不能证明之后不会发生数据竞争。所以为了使结果尽量正确,请保证你的测试并发地覆盖到了你到包。 竞争检查器会报告所有的已经发生的数据竞争。然而,它只能检测到运行时的竞争条件;并不能证明之后不会发生数据竞争。所以为了使结果尽量正确,请保证你的测试并发地覆盖到了你到包。
由于需要额外的记录,因此构建时加了竞争检测的程序跑起来会慢一些,且需要更大的内存,即是这样这些代价对于很多生产环境的工作来说还是可以接受的。对于一些偶发的竞争条件来说让竞争检查器来干活可以节省无数日夜的debugging。(译注多少服务端C和C艹程序员为此折腰) 由于需要额外的记录,因此构建时加了竞争检测的程序跑起来会慢一些,且需要更大的内存,即使是这样这些代价对于很多生产环境的工作来说还是可以接受的。对于一些偶发的竞争条件来说让竞争检查器来干活可以节省无数日夜的debugging。(译注多少服务端C和C艹程序员为此折腰)

View File

@ -163,9 +163,6 @@ type Memo struct {
// Get is concurrency-safe. // Get is concurrency-safe.
func (memo *Memo) Get(key string) (value interface{}, err error) { func (memo *Memo) Get(key string) (value interface{}, err error) {
res, ok := memo.cache[key]
if !ok {
res.value, res.err = memo.f(key)
memo.cache[key] = res memo.cache[key] = res
memo.mu.Lock() memo.mu.Lock()
res, ok := memo.cache[key] res, ok := memo.cache[key]

View File

@ -1,4 +1,4 @@
# 第九章 基于共享变量的并发 # 第九章 基于共享变量的并发
前一章我们介绍了一些使用goroutine和channel这样直接而自然的方式来实现并发的方法。然而这样做我们实际上屏蔽掉了在写并发代码时必须处理的一些重要而且细微的问题。 前一章我们介绍了一些使用goroutine和channel这样直接而自然的方式来实现并发的方法。然而这样做我们实际上屏蔽掉了在写并发代码时必须处理的一些重要而且细微的问题。

View File

@ -37,7 +37,6 @@
[Squeak]: http://doc.cat-v.org/bell_labs/squeak/ [Squeak]: http://doc.cat-v.org/bell_labs/squeak/
<!-- 系统 --> <!-- 系统 -->
[Unix]: http://doc.cat-v.org/unix/
[UNIX]: http://doc.cat-v.org/unix/ [UNIX]: http://doc.cat-v.org/unix/
[Linux]: http://www.linux.org/ [Linux]: http://www.linux.org/
[FreeBSD]: https://www.freebsd.org/ [FreeBSD]: https://www.freebsd.org/

View File

@ -16,6 +16,7 @@ Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本
- http://shifei.me/gopl-zh/ - http://shifei.me/gopl-zh/
- http://2goo.info/media/html/gopl-zh-gh-pages/ - http://2goo.info/media/html/gopl-zh-gh-pages/
- http://docs.plhwin.com/gopl-zh/ - http://docs.plhwin.com/gopl-zh/
- http://gopl-zh.simple-is-best.tk/
{% include "./version.md" %} {% include "./version.md" %}
@ -23,7 +24,7 @@ Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本
# 译者序 # 译者序
在上个世纪70年代贝尔实验室的[Ken Thompson][KenThompson]和[Dennis M. Ritchie][DennisRitchie]合作发明了[UNIX](http://doc.cat-v.org/unix/)操作系统,同时[Dennis M. Ritchie][DennisRitchie]为了解决[UNIX](http://doc.cat-v.org/unix/)系统的移植性问题而发明了C语言贝尔实验室的[UNIX](http://doc.cat-v.org/unix/)和C语言两大发明奠定了整个现代IT行业最重要的软件基础目前的三大桌面操作系统的中[Linux](http://www.linux.org/)和[Mac OS X](http://www.apple.com/cn/osx/)都是源于[UINX]()系统两大移动平台的操作系统iOS和Android也都是源于[UNIX](http://doc.cat-v.org/unix/)系统。C系家族的编程语言占据统治地位达几十年之久。在[UINX]()和C语言发明40年之后目前已经在Google工作的[Ken Thompson](http://genius.cat-v.org/ken-thompson/)和[Rob Pike](http://genius.cat-v.org/rob-pike/)(他们在贝尔实验室时就是同事)、还有[Robert Griesemer](http://research.google.com/pubs/author96.html)设计了V8引擎和HotSpot虚拟机一起合作为了解决在21世纪多核和网络化环境下越来越复杂的编程问题而发明了Go语言。从Go语言库早期代码库日志可以看出它的演化历程Git用`git log --before={2008-03-03} --reverse`命令查看): 在上个世纪70年代贝尔实验室的[Ken Thompson][KenThompson]和[Dennis M. Ritchie][DennisRitchie]合作发明了[UNIX](http://doc.cat-v.org/unix/)操作系统,同时[Dennis M. Ritchie][DennisRitchie]为了解决[UNIX](http://doc.cat-v.org/unix/)系统的移植性问题而发明了C语言贝尔实验室的[UNIX](http://doc.cat-v.org/unix/)和C语言两大发明奠定了整个现代IT行业最重要的软件基础目前的三大桌面操作系统的中[Linux](http://www.linux.org/)和[Mac OS X](http://www.apple.com/cn/osx/)都是源于[UNIX]()系统两大移动平台的操作系统iOS和Android也都是源于[UNIX](http://doc.cat-v.org/unix/)系统。C系家族的编程语言占据统治地位达几十年之久。在[UNIX]()和C语言发明40年之后目前已经在Google工作的[Ken Thompson](http://genius.cat-v.org/ken-thompson/)和[Rob Pike](http://genius.cat-v.org/rob-pike/)(他们在贝尔实验室时就是同事)、还有[Robert Griesemer](http://research.google.com/pubs/author96.html)设计了V8引擎和HotSpot虚拟机一起合作为了解决在21世纪多核和网络化环境下越来越复杂的编程问题而发明了Go语言。从Go语言库早期代码库日志可以看出它的演化历程Git用`git log --before={2008-03-03} --reverse`命令查看):
![](./images/go-log04.png) ![](./images/go-log04.png)
@ -35,7 +36,7 @@ Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本
该书英文版约从2015年10月开始公开发售其中日文版本最早参与翻译和审校参考致谢部分。在2015年10月我们并不知道中文版是否会及时引进、将由哪家出版社引进、引进将由何人来翻译、何时能出版这些信息都成了一个秘密。中国的Go语言社区是全球最大的Go语言社区我们从一开始就始终紧跟着Go语言的发展脚步。我们应该也完全有能力以中国Go语言社区的力量同步完成Go语言圣经中文版的翻译工作。与此同时国内有很多Go语言爱好者也在积极关注该书本人也在第一时间购买了纸质版本[亚马逊价格314人民币](http://www.amazon.cn/The-Go-Programming-Language-Donovan-Alan-A-A/dp/0134190440/)。补充:国内也即将出版英文版,[价格79元](http://product.china-pub.com/4912464)。为了Go语言的学习和交流大家决定合作免费翻译该书。 该书英文版约从2015年10月开始公开发售其中日文版本最早参与翻译和审校参考致谢部分。在2015年10月我们并不知道中文版是否会及时引进、将由哪家出版社引进、引进将由何人来翻译、何时能出版这些信息都成了一个秘密。中国的Go语言社区是全球最大的Go语言社区我们从一开始就始终紧跟着Go语言的发展脚步。我们应该也完全有能力以中国Go语言社区的力量同步完成Go语言圣经中文版的翻译工作。与此同时国内有很多Go语言爱好者也在积极关注该书本人也在第一时间购买了纸质版本[亚马逊价格314人民币](http://www.amazon.cn/The-Go-Programming-Language-Donovan-Alan-A-A/dp/0134190440/)。补充:国内也即将出版英文版,[价格79元](http://product.china-pub.com/4912464)。为了Go语言的学习和交流大家决定合作免费翻译该书。
翻译工作从2015年11月20日前后开始到2016年1月底初步完成前后历时约2个月时间在其它语言版本中全球第一个完成翻译的基本做到和原版同步。其中[chai2010](https://github.com/chai2010)翻译了前言、第2~4章、第10~13章[Xargin](https://github.com/cch123)翻译了第1章、第6章、第8~9章[CrazySssst](https://github.com/CrazySssst)翻译了第5章[foreversmart](https://github.com/foreversmart)翻译了第7章大家共同参与了基本的校验工作还有其他一些朋友提供了积极的反馈建议。如果大家还有任何问题或建议可以直接到中文版项目页面提交[Issue](https://github.com/golang-china/gopl-zh/issues),如果发现英文版原文在[勘误](http://www.gopl.io/errata.html)中未提到的任何错误,可以直接去[英文版项目](https://github.com/adonovan/gopl.io/)提交。 翻译工作从2015年11月20日前后开始到2016年1月底初步完成前后历时约2个月时间在其它语言版本中全球第一个完成翻译的基本做到和原版同步。其中[chai2010](https://github.com/chai2010)翻译了前言、第2 ~ 4章、第10 ~ 13章[Xargin](https://github.com/cch123)翻译了第1章、第6章、第8 ~ 9章[CrazySssst](https://github.com/CrazySssst)翻译了第5章[foreversmart](https://github.com/foreversmart)翻译了第7章大家共同参与了基本的校验工作还有其他一些朋友提供了积极的反馈建议。如果大家还有任何问题或建议可以直接到中文版项目页面提交[Issue](https://github.com/golang-china/gopl-zh/issues),如果发现英文版原文在[勘误](http://www.gopl.io/errata.html)中未提到的任何错误,可以直接去[英文版项目](https://github.com/adonovan/gopl.io/)提交。
最后希望这本书能够帮助大家用Go语言快乐地编程。 最后希望这本书能够帮助大家用Go语言快乐地编程。