Compare commits

59 Commits

Author SHA1 Message Date
柴树杉
6ecbba5baa Merge pull request #247 from Nyvryl/Nyvryl-patch-1
Revise chapter 5.5 on function values in Go
2025-11-16 20:46:39 +08:00
Nyvryl
522457fa25 Revise chapter 5.5 on function values in Go
此处应为 "forEachNode" 而非 ”forEachNoded“,后者在前文中完全没有提及,应为笔误。
2025-11-16 11:13:04 +00:00
柴树杉
c58743acce Merge pull request #245 from lin-snow/patch-2
docs: add note about compile-time interface checks
2025-09-03 16:59:51 +08:00
柴树杉
cc738922ed Merge pull request #246 from lin-snow/patch-3
fix(docs): 更正术语“先觉条件”为“先决条件”
2025-09-03 16:59:14 +08:00
L1nSn0w
c080b2ac6d docs: 将“先觉条件”更正为“先决条件”,以符合技术语境 2025-09-03 09:52:02 +08:00
L1nSn0w
1ed9861965 Document conditions for implementing interfaces
Add section on interface implementation conditions
2025-09-03 09:10:55 +08:00
柴树杉
9e504959ee Merge pull request #242 from DormancyWang/master
修改:第七章介绍
2025-09-02 16:22:04 +08:00
柴树杉
e13b3dc49a Merge pull request #244 from lin-snow/patch-1
fix: correct typo "而不再试" → "而不再是"
2025-09-02 16:21:15 +08:00
L1nSn0w
a08a8f4162 Revise chapter 5.6 on anonymous functions and closures
This section introduces anonymous functions in Go, explaining their syntax, usage, and implications in terms of closures and lexical environments. It also discusses common pitfalls related to variable capture in loops and provides examples of breadth-first search and topological sorting.
2025-09-02 15:44:42 +08:00
Guo
bdf5fdf226 修改:第七章介绍
接口是go语言中十分重要的一个概念,与其相关的介绍应该逻辑简单,通俗
易懂。原翻译有些生硬,不好理解。反而加重读者的阅读负担。
这里应该用流畅的语言重点介绍想法,而无需刻意追求严谨性。
所以对介绍做了一些修改。有了主要的想法,后续的理解的过程中会更有方
向。
2025-08-06 11:13:47 +08:00
柴树杉
f15fcec338 Update book.ini 2025-07-28 12:56:18 +08:00
chai2010
614ecbe1ea Update preface.md 2024-10-27 10:32:41 +08:00
chai2010
6869239980 Merge pull request #229 from LiAuTraver/master
Remove redundant comments
2024-10-10 10:46:31 +08:00
LiAuTraver
0cb5d25bfb remove redundant comments 2024-10-09 20:45:11 +08:00
chai2010
56bbafbd08 use wabook 2024-08-13 08:15:19 +08:00
chai2010
dbdfa93bc1 Update README.md 2024-08-13 06:21:31 +08:00
chai2010
b76e3beb2c 切换到 mnbook 构建 2024-08-08 15:46:18 +08:00
chai2010
6849a4049a Merge pull request #221 from DaTouJun/patch-1
Update ch1-03.md
2024-08-08 15:27:38 +08:00
chai2010
d14d1d0a9c Update index.hbs 2024-06-12 15:19:50 +08:00
关胜
523e81ec43 Update ch1-03.md
如果在windows下默认换行可能是\r\n,如果仍然使用\n最后没有空行,其他行分割后都多\r,最后一行分割后没有\r将会计数错误
2024-05-28 19:12:37 +08:00
chai2010
fa27be9e3f Merge pull request #219 from gopl-zh/revert-213-patch-1
Revert "Update ch5-06.md"
2024-04-03 16:10:24 +08:00
chai2010
687fb92030 Revert "Update ch5-06.md" 2024-04-03 16:09:26 +08:00
chai2010
feac38bcf2 Merge pull request #213 from A-caibird/patch-1
Update ch5-06.md
2024-02-05 13:44:47 +08:00
Acaibrid
8df9c1211c Update ch5-06.md
返回的是一个函数,而不是返回的这个函数被调用后的返回值
2024-02-05 01:32:21 +08:00
chai2010
b8d7c32434 Merge pull request #210 from hanzhuoxian/master
第一章第二节 逻辑修正
2023-11-29 16:17:14 +08:00
chai2010
d35ce39441 Merge pull request #211 from yudianer/patch-1
Update ch3.md
2023-11-29 16:16:36 +08:00
Jack
b1b9274c8c Update ch3.md
“words” 应该指计算机里面的“字”,比如“一个字长”
2023-11-27 00:39:21 -06:00
韩卓贤
233eef7844 Merge branch 'gopl-zh:master' into master 2023-11-02 00:31:19 -05:00
hanzhuoxian
0bf3867967 变量形式描述逻辑修正 2023-11-02 13:30:40 +08:00
chai2010
c7f58f8991 Merge pull request #209 from hanzhuoxian/master
命令行参数 修正
2023-10-31 23:25:29 +08:00
hanjian
a9f4ae0bf7 命令行参数 修正 2023-10-31 20:50:14 +08:00
chai2010
bb175c69f5 Merge pull request #202 from liaosunny123/patch-2
Update ch7-01.md
2023-08-11 19:24:49 +08:00
chai2010
d948a26c96 Merge pull request #201 from liaosunny123/patch-1
Update ch1-05.md
2023-07-22 20:28:37 +08:00
EpicMo
213fe7cbaa Update ch7-01.md
修改句子
2023-07-22 15:11:55 +08:00
EpicMo
279919079a Update ch1-05.md 2023-07-20 10:20:08 +08:00
chai2010
2decff99fd Merge pull request #199 from yunmingyang/fixSynatx
Drop a '。' which may cause ambiguity in chapter 1.1
2023-06-27 22:44:30 +08:00
yunmingyang
c19fb7aff1 Drop a '。' which may cause ambiguity in chapter 1.1 2023-06-27 11:48:15 +08:00
chai2010
f4a7cd48b4 Update index.hbs 2023-06-27 01:46:06 +08:00
chai2010
0984caea57 Update README.md 2023-06-27 01:43:33 +08:00
chai2010
dba2f4c2f1 Update README.md 2023-06-27 01:42:32 +08:00
chai2010
a10ff75618 Update index.md 2023-06-27 01:42:08 +08:00
chai2010
b7cdabf84a Update preface-zh.md 2023-06-27 01:37:50 +08:00
chai2010
023499ad54 Merge pull request #194 from xingchengxia/master
fix: typo
2023-06-27 01:31:04 +08:00
chai2010
863c1951ea Update SUMMARY.md 2023-06-27 01:30:35 +08:00
chai2010
cd809c6adc Create preface-zh.md
恢复误删的译者序
2023-06-27 01:30:01 +08:00
FireAndIceCoder
4139bfabf3 fix: typo
Update ch5-02.md
2023-05-07 22:46:29 +08:00
chai2010
92199cf1ff Merge pull request #184 from whaliendev/fix-content-lost
fix: remove nbsp in SUMMARY.md
2023-03-31 18:23:09 +08:00
Hwa
81eda26917 fix: remove nbsp in SUMMARY.md 2023-03-31 15:41:28 +08:00
chai2010
145304b099 Merge pull request #135 from Glenn-Su/master
Update ch9-07.md
2022-10-24 07:50:11 +08:00
Glenn-Su
2310a57796 Update ch9-07.md
1. 与代码对应的字段不应翻译成中文
2. 部分 routine 拼写错误
2022-10-23 01:16:04 +08:00
Xargin
9b5c4ea466 Merge pull request #131 from StudentWeis/patch-1
一个小翻译问题和 typo
2022-10-20 14:05:33 +08:00
HengYi Wei
a1ad719a03 Update ch5-09.md
翻译问题
2022-10-08 13:57:32 +08:00
HengYi Wei
9490a5390d Update ch6-01.md
typo
2022-10-08 13:52:56 +08:00
chai2010
1597ed7bcd Merge pull request #119 from Asakijz/patch-7
Update ch5-10.md
2022-10-06 15:02:29 +08:00
chai2010
b4c6e3e234 Merge pull request #128 from Asakijz/patch-11
Update ch9-08.md
2022-10-06 15:01:42 +08:00
chai2010
eaef4fd12d Merge pull request #130 from wingrez/patch-1
fix ch3.5.1 translation error
2022-10-06 15:01:08 +08:00
Wingrez
380ff85241 fix ch3.5.1 translate error 2022-10-06 11:59:29 +08:00
Asakijz
414ff02cea Update ch9-08.md
加粗样式丢失
2022-09-22 15:13:15 +08:00
Asakijz
f6bb64ce09 Update ch5-10.md
原文为:deferred函数帮助Parse从panic中恢复。

应该是 recover函数吧
2022-09-05 16:47:45 +08:00
32 changed files with 109 additions and 610 deletions

View File

@@ -18,13 +18,16 @@ jobs:
- name: Git checkout
uses: actions/checkout@v2
- name: Setup mdBook
uses: peaceiris/actions-mdbook@v1
- name: Set up Go
uses: actions/setup-go@v2
with:
mdbook-version: '0.4.10'
# mdbook-version: 'latest'
go-version: 1.21
- run: mdbook build
- run: go version
- run: go env
- run: go install github.com/wa-lang/wabook@latest
- run: wabook build
- name: Deploy
uses: peaceiris/actions-gh-pages@v3

View File

@@ -2,9 +2,19 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# install mkbook
#
# WaBook: Mini Markdown Book
# https://github.com/wa-lang/wabook
#
default:
mdbook serve
wabook serve
build:
-rm book
wabook build
-rm book/.gitignore
-rm -rf book/.git
clean:
-rm -rf book

View File

@@ -1,12 +1,11 @@
# Go语言圣经中文版
- *KusonStack一站式可编程配置技术栈(Go): https://github.com/KusionStack/kusion*
- *KCL 配置编程语言(Rust): https://github.com/KusionStack/KCLVM*
- *凹语言™: https://github.com/wa-lang/wa*
- *凹语言(专为 WebAssembly 设计): https://github.com/wa-lang/wa*
- *WaBook(Go语言实现的MD电子书构建工具): https://github.com/wa-lang/wabook*
----
Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本仅供学习交流之用。对于希望学习CGO、Go汇编语言等高级用法的同学我们推荐[《Go语言高级编程》](https://github.com/chai2010/advanced-go-programming-book)开源图书。如果希望深入学习Go语言语法树结构可以参考[《Go语法树入门——开启自制编程语言和编译器之旅》](https://github.com/chai2010/go-ast-book)。如果想从头实现一个玩具Go语言可以参考[《从头实现µGo语言》](https://github.com/chai2010/ugo-compiler-book)。
Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本仅供学习交流之用。对于希望学习CGO、Go汇编语言等高级用法的同学我们推荐[《Go语言高级编程》](https://github.com/chai2010/advanced-go-programming-book)开源图书。如果希望深入学习Go语言语法树结构可以参考[《Go语法树入门——开启自制编程语言和编译器之旅》](https://github.com/chai2010/go-ast-book)。如果想从头实现一个玩具Go语言可以参考[《从头实现µGo语言》](https://github.com/wa-lang/ugo-compiler-book)µGo 是 [凹语言](https://wa-lang.org/) 阶段的产物)
![](cover.jpg)

View File

@@ -1,6 +1,7 @@
# Summary
[Go语言圣经](index.md)
[译者序](preface-zh.md)
[前言](preface.md)
* [入门](ch1/ch1.md)
@@ -76,7 +77,7 @@
* [并发的循环](ch8/ch8-05.md)
* [示例: 并发的Web爬虫](ch8/ch8-06.md)
* [基于select的多路复用](ch8/ch8-07.md)
 * [示例: 并发的目录遍历](ch8/ch8-08.md)
* [示例: 并发的目录遍历](ch8/ch8-08.md)
* [并发的退出](ch8/ch8-09.md)
* [示例: 聊天服务](ch8/ch8-10.md)
* [基于共享变量的并发](ch9/ch9.md)
@@ -84,7 +85,7 @@
* [sync.Mutex互斥锁](ch9/ch9-02.md)
* [sync.RWMutex读写锁](ch9/ch9-03.md)
* [内存同步](ch9/ch9-04.md)
 * [sync.Once惰性初始化](ch9/ch9-05.md)
* [sync.Once惰性初始化](ch9/ch9-05.md)
* [竞争条件检测](ch9/ch9-06.md)
* [示例: 并发的非阻塞缓存](ch9/ch9-07.md)
* [Goroutines和线程](ch9/ch9-08.md)
@@ -110,7 +111,7 @@
* [示例: 编码S表达式](ch12/ch12-04.md)
* [通过reflect.Value修改值](ch12/ch12-05.md)
* [示例: 解码S表达式](ch12/ch12-06.md)
 * [获取结构体字段标签](ch12/ch12-07.md)
* [获取结构体字段标签](ch12/ch12-07.md)
* [显示一个类型的方法集](ch12/ch12-08.md)
* [几点忠告](ch12/ch12-09.md)
* [底层编程](ch13/ch13.md)

30
book.ini Normal file
View File

@@ -0,0 +1,30 @@
# https://giscus.app
# https://github.com/badboy/mdbook-mermaid
[book]
title = "Go语言圣经"
authors = ["译者:", "chai2010", "Xargin", "CrazySssst", "foreversmart"]
description = "<The Go Programming Language>中文版"
language = "zh"
multilingual = false
src = "."
[build]
build-dir = "book"
[custom]
content_header = "<ul dir=\"auto\"><li><em>凹语言(Go实现, 面向WASM设计): <a href=\"https://github.com/wa-lang/wa\">https://github.com/wa-lang/wa</a></em></li><li><em>《Go语言高级编程》: <a href=\"https://github.com/chai2010/advanced-go-programming-book\">https://github.com/chai2010/advanced-go-programming-book</a></em></li></ul><hr>"
content_footer = "<hr><table><tr><td><img width=\"222px\" src=\"https://chai2010.cn/advanced-go-programming-book/css.png\"></td><td><img width=\"222px\" src=\"https://chai2010.cn/advanced-go-programming-book/cch.png\"></td></tr></table>"
page_footer = "<span>© 2015-2016 | <a href=\"https://github.com/gopl-zh\"> Go语言圣经中文版</a>, 仅学习交流使用</span>"
[giscus]
enaled = true
data_repo = "gopl-zh/gopl-zh.github.com"
data_repo_id = "MDEwOlJlcG9zaXRvcnk2MTUzMTQ2Mw=="
data_category = "General"
data_category_id = "DIC_kwDOA6rlR84CQnJW"
[output.html]
git-repository-url = "https://github.com/gopl-zh/gopl-zh.github.com"
edit-url-template = "https://github.com/gopl-zh/gopl-zh.github.com/edit/master/{path}"
git-repository-icon = "fa-github"

View File

@@ -1,20 +0,0 @@
# https://giscus.app
# https://github.com/badboy/mdbook-mermaid
[book]
title = "Go语言圣经"
authors = ["译者:", "chai2010", "Xargin", "CrazySssst", "foreversmart"]
description = "<The Go Programming Language>中文版"
language = "zh"
multilingual = false
src = "."
[build]
build-dir = "book"
[output.html]
additional-css = ["style.css"]
additional-js = ["js/custom.js", "js/bigPicture.js"]
git-repository-url = "https://github.com/gopl-zh/gopl-zh.github.com"
edit-url-template = "https://github.com/gopl-zh/gopl-zh.github.com/edit/master/{path}"
git-repository-icon = "fa-github"

View File

@@ -65,7 +65,7 @@ Go 的标准库提供了 100 多个包,以支持常见功能,如输入、输
Go 语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号,因此换行符添加的位置会影响 Go 代码的正确解析(译注:比如行末是标识符、整数、浮点数、虚数、字符或字符串文字、关键字 `break``continue``fallthrough``return` 中的一个、运算符和分隔符 `++``--``)``]``}` 中的一个)。举个例子,函数的左括号 `{` 必须和 `func` 函数声明在同一行上,且位于末尾,不能独占一行,而在表达式 `x+y` 中,可在 `+` 后换行,不能在 `+` 前换行(译注:以+结尾的话不会被插入分号分隔符,但是以 x 结尾的话则会被分号分隔符,从而导致编译错误)。
Go 语言在代码格式上采取了很强硬的态度。`gofmt`工具把代码格式化为标准格式译注这个格式化工具没有任何可以调整代码格式的参数Go 语言就是这么任性),并且 `go` 工具中的 `fmt` 子命令会对指定包,否则默认为当前目录中所有go 源文件应用 `gofmt` 命令。本书中的所有代码都被 gofmt 过。你也应该养成格式化自己的代码的习惯。以法令方式规定标准的代码格式可以避免无尽的无意义的琐碎争执(译注:也导致了 Go 语言的 TIOBE 排名较低,因为缺少撕逼的话题)。更重要的是,这样可以做多种自动源码转换,如果放任 Go 语言代码格式,这些转换就不大可能了。
Go 语言在代码格式上采取了很强硬的态度。`gofmt`工具把代码格式化为标准格式译注这个格式化工具没有任何可以调整代码格式的参数Go 语言就是这么任性),并且 `go` 工具中的 `fmt` 子命令会对指定包否则默认为当前目录中所有go 源文件应用 `gofmt` 命令。本书中的所有代码都被 gofmt 过。你也应该养成格式化自己的代码的习惯。以法令方式规定标准的代码格式可以避免无尽的无意义的琐碎争执(译注:也导致了 Go 语言的 TIOBE 排名较低,因为缺少撕逼的话题)。更重要的是,这样可以做多种自动源码转换,如果放任 Go 语言代码格式,这些转换就不大可能了。
很多文本编辑器都可以配置为保存文件时自动执行 `gofmt`,这样你的源代码总会被恰当地格式化。还有个相关的工具:`goimports`,可以根据代码需要,自动地添加或删除 `import` 声明。这个工具并没有包含在标准的分发包中,可以用下面的命令安装:

View File

@@ -35,7 +35,7 @@ func main() {
`var` 声明定义了两个 `string` 类型的变量 `s``sep`。变量会在声明时直接初始化。如果变量没有显式初始化,则被隐式地赋予其类型的 *零值*zero value数值类型是 `0`,字符串类型是空字符串 `""`。这个例子里,声明把 `s``sep` 隐式地初始化成空字符串。第 2 章再来详细地讲解变量和声明。
对数值类型Go 语言提供了常规的数值和逻辑运算符。而对 `string` 类型,`+` 运算符连接字符串(译注:和 C++ 或者 JavaScript 是一样的)。所以表达式:`sep + os.Args[i]` 表示连接字符串 `sep``os.Args`。程序中使用的语句:`s+=sep+os.Args[i]` 是一条 *赋值语句*,将 `s` 的旧值跟 `sep``os.Args[i]` 连接后赋值回 `s`,等价于:`s=s+sep+os.Args[i]`
对数值类型Go 语言提供了常规的数值和逻辑运算符。而对 `string` 类型,`+` 运算符连接字符串(译注:和 C++ 或者 JavaScript 是一样的)。所以表达式:`sep + os.Args[i]` 表示连接字符串 `sep``os.Args[i]`。程序中使用的语句:`s+=sep+os.Args[i]` 是一条 *赋值语句*,将 `s` 的旧值跟 `sep``os.Args[i]` 连接后赋值回 `s`,等价于:`s=s+sep+os.Args[i]`
运算符 `+=` 是赋值运算符assignment operator每种数值运算符或逻辑运算符`+``*`,都有对应的赋值运算符。
@@ -113,7 +113,7 @@ var s = ""
var s string = ""
```
用哪种不用哪种,为什么呢?第一种形式,是一条短变量声明,最简洁,但只能用在函数内部,而不能用于包变量。第二种形式依赖于字符串的默认初始化零值机制,被初始化为 `""`。第三种形式用得很少,除非同时声明多个变量。第四种形式显式地标明变量的类型,当变量类型与初值类型相同时,类型冗余,但如果两者类型不同,变量类型就必须了。实践中一般使用前两种形式中的某个,初始值重要的话就显式地指定变量的类型,否则使用隐式初始化。
用哪种不用哪种,为什么呢?第一种形式,是一条短变量声明,最简洁,但只能用在函数内部,而不能用于包变量。第二种形式依赖于字符串的默认初始化零值机制,被初始化为 `""`。第三种形式用得很少,除非同时声明多个变量。第四种形式显式地标明变量的类型,当变量类型与初值类型相同时,类型冗余,但如果两者类型不同,变量类型就必须了。实践中一般使用前两种形式中的某个,初始值重要的话就显式地指定变量的,否则指定类型使用隐式初始化。
如前文所述,每次循环迭代字符串 `s` 的内容都会更新。`+=` 连接原字符串、空格和下个参数,产生新字符串,并把它赋值给 `s``s` 原来的内容已经不再使用,将在适当时机对它进行垃圾回收。

View File

@@ -135,7 +135,7 @@ func countLines(f *os.File, counts map[string]int) {
`map` 是一个由 `make` 函数创建的数据结构的引用。`map` 作为参数传递给某函数时该函数接收这个引用的一份拷贝copy或译为副本被调用函数对 `map` 底层数据结构的任何修改,调用者函数都可以通过持有的 `map` 引用看到。在我们的例子中,`countLines` 函数向 `counts` 插入的值,也会被 `main` 函数看到。(译注:类似于 C++ 里的引用传递,实际上指针是另一个指针了,但内部存的值指向同一块内存)
`dup` 的前两个版本以"流”模式读取输入,并根据需要拆分成多个行。理论上,这些程序可以处理任意数量的输入数据。还有另一个方法,就是一口气把全部输入数据读到内存中,一次分割为多行,然后处理它们。下面这个版本,`dup3`,就是这么操作的。这个例子引入了 `ReadFile` 函数(来自于`io/ioutil`包),其读取指定文件的全部内容,`strings.Split` 函数把字符串分割成子串的切片。(`Split` 的作用与前文提到的 `strings.Join` 相反。)
`dup` 的前两个版本以"流”模式读取输入,并根据需要拆分成多个行。理论上,这些程序可以处理任意数量的输入数据。还有另一个方法,就是一口气把全部输入数据读到内存中,一次分割为多行,然后处理它们。下面这个版本,`dup3`,就是这么操作的。这个例子引入了 `ReadFile` 函数(来自于`io/ioutil`包),其读取指定文件的全部内容,`strings.Split` 函数把字符串分割成子串的切片。(`Split` 的作用与前文提到的 `strings.Join` 相反。请注意如果在Windows下测试注意换行是否为\r\n否则最后一行是否有空行将会影响结果。
我们略微简化了 `dup3`。首先,由于 `ReadFile` 函数需要文件名作为参数,因此只读指定文件,不读标准输入。其次,由于行计数代码只在一处用到,故将其移回 `main` 函数。

View File

@@ -23,7 +23,7 @@ func main() {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
@@ -34,7 +34,7 @@ func main() {
}
```
这个程序从两个package中导入了函数net/http和io/ioutil包http.Get函数是创建HTTP请求的函数如果获取过程没有出错那么会在resp这个结构体中得到访问的请求结果。resp的Body字段包括一个可读的服务器响应流。ioutil.ReadAll函数从response中读取到全部内容将其结果保存在变量b中。resp.Body.Close关闭resp的Body流防止资源泄露Printf函数会将结果b写出到标准输出流中。
这个程序从两个package中导入了函数net/http和iohttp.Get函数是创建HTTP请求的函数如果获取过程没有出错那么会在resp这个结构体中得到访问的请求结果。resp的Body字段包括一个可读的服务器响应流。io.ReadAll函数从response中读取到全部内容将其结果保存在变量b中。resp.Body.Close关闭resp的Body流防止资源泄露Printf函数会将结果b写出到标准输出流中。
```
$ go build gopl.io/ch1/fetch

View File

@@ -35,7 +35,7 @@ x, y = y, x
a[i], a[j] = a[j], a[i]
```
或者是计算两个整数值的的最大公约数GCD译注GCD不是那个敏感字而是greatest common divisor的缩写欧几里德的GCD是最早的非平凡算法
或者是计算两个整数值的的最大公约数GCD译注Greatest Common Divisor的缩写欧几里德的GCD是最早的非平凡算法
```Go
func gcd(x, y int) int {
@@ -58,7 +58,7 @@ func fib(n int) int {
}
```
元组赋值也可以使一系列琐碎赋值更加紧凑(译注: 特别是在for循环的初始化部分
元组赋值也可以使一系列琐碎赋值更加紧凑(译注: 特别是在for循环的初始化部分:
```Go
i, j, k = 2, 3, 5

View File

@@ -71,7 +71,7 @@ s[0] = 'L' // compile error: cannot assign to s[0]
字符串值也可以用字符串面值方式编写只要将一系列字节序列包含在双引号内即可
```
"Hello, 世界"
"Hello, world"
```
![](../images/ch3-04.png)

View File

@@ -1,5 +1,5 @@
# 第3章 基础数据类型
虽然从底层而言,所有的数据都是由比特组成,但计算机一般操作的是固定大小的如整数、浮点数、比特数组、内存地址等。进一步将这些数组织在一起就可表达更多的对象例如数据包、像素点、诗歌甚至其他任何对象。Go语言提供了丰富的数据组织形式这依赖于Go语言内置的数据类型。这些内置的数据类型兼顾了硬件的特性和表达复杂数据结构的便捷性。
虽然从底层而言,所有的数据都是由比特组成,但计算机一般操作的是固定大小的如整数、浮点数、比特数组、内存地址等。进一步将这些数组织在一起就可表达更多的对象例如数据包、像素点、诗歌甚至其他任何对象。Go语言提供了丰富的数据组织形式这依赖于Go语言内置的数据类型。这些内置的数据类型兼顾了硬件的特性和表达复杂数据结构的便捷性。
Go语言将数据类型分为四类基础类型、复合类型、引用类型和接口类型。本章介绍基础类型包括数字、字符串和布尔型。复合数据类型——数组§4.1和结构体§4.2——是通过组合简单类型来表达更加复杂的数据结构。引用类型包括指针§2.3.2、切片§4.2)、字典§4.3、函数§5、通道§8虽然数据种类很多但它们都是对程序中一个变量或状态的间接引用。这意味着对任一引用类型数据的修改都会影响所有该引用的拷贝。我们将在第7章介绍接口类型。

View File

@@ -4,7 +4,7 @@
下文的示例代码使用了非标准包 golang.org/x/net/html 解析HTML。golang.org/x/... 目录下存储了一些由Go团队设计、维护对网络编程、国际化文件处理、移动平台、图像处理、加密解密、开发者工具提供支持的扩展包。未将这些扩展包加入到标准库原因有二一是部分包仍在开发中二是对大多数Go语言的开发者而言扩展包提供的功能很少被使用。
例子中调用golang.org/x/net/html的部分api如下所示。html.Parse函数读入一组bytes解析后返回html.Node类型的HTML页面树状结构根节点。HTML拥有很多类型的结点如text文本、commnets注释类型在下面的例子中我们 只关注< name key='value' >形式的结点。
例子中调用golang.org/x/net/html的部分api如下所示。html.Parse函数读入一组bytes解析后返回html.Node类型的HTML页面树状结构根节点。HTML拥有很多类型的结点如text文本、comments注释类型在下面的例子中我们 只关注< name key='value' >形式的结点。
<u><i>golang.org/x/net/html</i></u>
```Go

View File

@@ -119,7 +119,7 @@ $ ./outline2 http://gopl.io
**练习 5.7** 完善startElement和endElement函数使其成为通用的HTML输出器。要求输出注释结点文本结点以及每个元素的属性< a href='...'>)。使用简略格式输出没有孩子结点的元素(即用`<img/>`代替`<img></img>`。编写测试验证程序输出的格式正确。详见11章
**练习 5.8** 修改pre和post函数使其返回布尔类型的返回值。返回false时中止forEachNoded的遍历。使用修改后的代码编写ElementByID函数根据用户输入的id查找第一个拥有该id元素的HTML元素查找成功后停止遍历。
**练习 5.8** 修改pre和post函数使其返回布尔类型的返回值。返回false时中止forEachNode的遍历。使用修改后的代码编写ElementByID函数根据用户输入的id查找第一个拥有该id元素的HTML元素查找成功后停止遍历。
```Go
func ElementByID(doc *html.Node, id string) *html.Node
@@ -130,3 +130,4 @@ func ElementByID(doc *html.Node, id string) *html.Node
```Go
func expand(s string, f func(string) string) string
```

View File

@@ -101,7 +101,7 @@ visitAll := func(items []string) {
}
```
在toposort程序的输出如下所示它的输出顺序是大多人想看到的固定顺序输出但是这需要我们多花点心思才能做到。哈希表prepreqs的value是遍历顺序固定的切片而不再遍历顺序随机的map所以我们对prereqs的key值进行排序保证每次运行toposort程序都以相同的遍历顺序遍历prereqs。
在toposort程序的输出如下所示它的输出顺序是大多人想看到的固定顺序输出但是这需要我们多花点心思才能做到。哈希表prepreqs的value是遍历顺序固定的切片而不再遍历顺序随机的map所以我们对prereqs的key值进行排序保证每次运行toposort程序都以相同的遍历顺序遍历prereqs。
```
1: intro to programming
@@ -302,3 +302,4 @@ for i := 0; i < len(dirs); i++ {
```
如果你使用go语句第八章或者defer语句5.8节会经常遇到此类问题。这不是go或defer本身导致的而是因为它们都会等待循环结束后再执行函数值。

View File

@@ -17,7 +17,7 @@ default:
}
```
断言函数必须满足的前置条件是明智的做法,但这很容易被滥用。除非你能提供更多的错误信息,或者能更快速的发现错误,否则不需要使用断言,编译器在运行时会帮你检查代码
断言函数必须满足的前置条件是明智的做法,但这很容易被滥用。除非你能提供更多的错误信息,或者能更快速的发现错误,否则不需要断言那些运行时会检查的条件
```Go
func Reset(x *Buffer) {

View File

@@ -17,7 +17,7 @@ func Parse(input string) (s *Syntax, err error) {
}
```
deferred函数帮助Parse从panic中恢复。在deferred函数内部panic value被附加到错误信息中并用err变量接收错误信息返回给调用者。我们也可以通过调用runtime.Stack往错误信息中添加完整的堆栈调用信息。
recover函数帮助Parse从panic中恢复。在deferred函数内部panic value被附加到错误信息中并用err变量接收错误信息返回给调用者。我们也可以通过调用runtime.Stack往错误信息中添加完整的堆栈调用信息。
不加区分的恢复所有的panic异常不是可取的做法因为在panic之后无法保证包级变量的状态仍然和我们预期一致。比如对数据结构的一次重要更新没有被完整完成、文件或者网络连接没有被关闭、获得的锁没有被释放。此外如果写日志时产生的panic被不加区分的恢复可能会导致漏洞被忽略。

View File

@@ -85,4 +85,4 @@ fmt.Println(geometry.PathDistance(perim)) // "12", standalone function
fmt.Println(perim.Distance()) // "12", method of geometry.Path
```
**译注:** 如果我们要用方法去计算perim的distance还需要去写全geometry的包名和其函数名但是因为Path这个类型定义了一个可以直接用的Distance方法所以我们可以直接写perim.Distance()。相当于可以少打很多字作者应该是这个意思。因为在Go里包外调用函数需要带上包名还是挺麻烦的。
**译注:** 如果我们要用函数去计算perim的distance还需要去写全geometry的包名和其函数名但是因为Path这个类型定义了一个可以直接用的Distance方法所以我们可以直接写perim.Distance()。相当于可以少打很多字作者应该是这个意思。因为在Go里包外调用函数需要带上包名还是挺麻烦的。

View File

@@ -20,10 +20,9 @@ func Sprintf(format string, args ...interface{}) string {
}
```
Fprintf的前缀F表示文件File也表明格式化输出结果应该被写入第一个参数提供的文件中。在Printf函数中的第一个参数os.Stdout是`*os.File`类型在Sprintf函数中的第一个参数&buf是一个指向可以写入字节的内存缓冲区然而它
并不是一个文件类型尽管它在某种意义上和文件类型相似。
Fprintf的前缀F表示文件File也表明格式化输出结果应该被写入第一个参数提供的文件中。在Printf函数中的第一个参数os.Stdout是`*os.File`类型在Sprintf函数中的第一个参数&buf是一个指向可以写入字节的内存缓冲区然而它并不是一个文件类型,尽管它在某种意义上和文件类型相似。
即使Fprintf函数中的第一个参数也不是一个文件类型。它是io.Writer类型这是一个接口类型定义如下
即使Fprintf函数中的第一个参数,它也不是一个文件类型。它是io.Writer类型这是一个接口类型定义如下
``` go
package io

View File

@@ -79,7 +79,7 @@ any = new(bytes.Buffer)
对于创建的一个interface{}值持有一个booleanfloatstringmappointer或者任意其它的类型我们当然不能直接对它持有的值做操作因为interface{}没有任何方法。我们会在7.10章中学到一种用类型断言来获取interface{}中值的方法。
因为接口与实现只依赖于判断两个类型的方法,所以没有必要定义一个具体类型和它实现的接口之间的关系。也就是说,有意地在文档里说明或者程序上断言这种关系偶尔是有用的,但程序上不强制这么做。下面的定义在编译期断言一个`*bytes.Buffer`的值实现了io.Writer接口类型:
因为接口与实现只依赖于判断两个类型的方法,所以没有必要定义一个具体类型和它实现的接口之间的关系。也就是说,有意地在文档里说明或者程序上断言这种关系偶尔是有用的,但程序上不强制这么做。这种写法还可用于让编译器在编译期确保某个类型确实满足接口要求,从而提前发现实现遗漏或接口变更导致的不匹配问题。下面的定义在编译期断言一个`*bytes.Buffer`的值实现了io.Writer接口类型:
```go
// *bytes.Buffer must satisfy io.Writer
@@ -150,3 +150,4 @@ type Streamer interface {
```
每一个具体类型的组基于它们相同的行为可以表示成一个接口类型。不像基于类的语言他们一个类实现的接口集合需要进行显式的定义在Go语言中我们可以在需要的时候定义一个新的抽象或者特定特点的组而不需要修改具体类型的定义。当具体的类型来自不同的作者时这种方式会特别有用。当然也确实没有必要在具体的类型中指出这些共性。

View File

@@ -151,7 +151,7 @@ if out != nil {
动态分配机制依然决定(\*bytes.Buffer).Write的方法会被调用但是这次的接收者的值是nil。对于一些如\*os.File的类型nil是一个有效的接收者§6.2.1),但是\*bytes.Buffer类型不在这些种类中。这个方法会被调用但是当它尝试去获取缓冲区时会发生panic。
问题在于尽管一个nil的\*bytes.Buffer指针有实现这个接口的方法它也不满足这个接口具体的行为上的要求。特别是这个调用违反了(\*bytes.Buffer).Write方法的接收者非空的隐含先条件所以将nil指针赋给这个接口是错误的。解决方案就是将main函数中的变量buf的类型改为io.Writer因此可以避免一开始就将一个不完整的值赋值给这个接口
问题在于尽管一个nil的\*bytes.Buffer指针有实现这个接口的方法它也不满足这个接口具体的行为上的要求。特别是这个调用违反了(\*bytes.Buffer).Write方法的接收者非空的隐含先条件所以将nil指针赋给这个接口是错误的。解决方案就是将main函数中的变量buf的类型改为io.Writer因此可以避免一开始就将一个不完整的值赋值给这个接口
```go
var buf io.Writer
@@ -162,3 +162,4 @@ f(buf) // OK
```
现在我们已经把接口值的技巧都讲完了让我们来看更多的一些在Go标准库中的重要接口类型。在下面的三章中我们会看到接口类型是怎样用在排序web服务错误处理中的。

View File

@@ -1,7 +1,7 @@
# 第7章 接口
接口类型是对其类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力
接口类型表达的是对其类型行为的一种泛化或抽象。通过泛化,接口使我们能够编写更灵活、更适应变化的函数,因为它们不依赖于某个特定实现的细节
多面向对象的语言都有相似的接口概念但Go语言中接口类型的独特之处在于它是满足隐式实现的。也就是说,我们没有必要对于给定的具体类型定义所有满足的接口类型;简单地拥有一些必需的方法就足够了。这种设计可以让你创建一个新的接口类型满足已经存在的具体类型却不会去改变这些类型的定义;当我们使用的类型来自于不受我们控制的包时这种设计尤其有用
多面向对象的语言都有“接口”这一概念,但 Go 的接口之所以独特,是因为它们是隐式满足的。换句话说,一个具体类型不需要显式声明它实现了哪些接口;只要它拥有接口所需的方法,就自动被认为满足了该接口。这种设计让你可以为已有的具体类型创建新的接口,而不需要修改这些类型本身——这在处理你无法控制的外部包中定义的类型时特别有用
在本章我们会开始看到接口类型和值的一些基本技巧。顺着这种方式我们将学习几个来自标准库的重要接口。很多Go程序中都尽可能多的去使用标准库中的接口。最后我们会在§7.10看到类型断言的知识§7.13)看到类型开关的使用并且学到他们是怎样让不同的类型的概括成为可能。

View File

@@ -199,7 +199,7 @@ func (memo *Memo) Get(key string) (value interface{}, err error) {
这些修改使性能再次得到了提升但有一些URL被获取了两次。这种情况在两个以上的goroutine同一时刻调用Get来请求同样的URL时会发生。多个goroutine一起查询cache发现没有值然后一起调用f这个慢不拉叽的函数。在得到结果后也都会去更新map。其中一个获得的结果会覆盖掉另一个的结果。
理想情况下是应该避免掉多余的工作的。而这种“避免”工作一般被称为duplicate suppression重复抑制/避免。下面版本的Memo每一个map元素都是指向一个条目的指针。每一个条目包含对函数f调用结果的内容缓存。与之前不同的是这次entry还包含了一个叫ready的channel。在条目的结果被设置之后这个channel就会被关闭以向其它goroutine广播§8.9)去读取该条目内的结果是安全的了。
理想情况下是应该避免掉多余的工作的。而这种“避免”工作一般被称为duplicate suppression重复抑制/避免。下面版本的Memo每一个map元素都是指向一个条目的指针。每一个entry包含对函数f调用结果的内容缓存。与之前不同的是这次entry还包含了一个叫ready的channel。在entry的res字段被设置之后这个channel就会被关闭以向其它goroutine广播§8.9)去读取该entry内的结果是安全的了。
<u><i>gopl.io/ch9/memo4</i></u>
```go
@@ -334,7 +334,7 @@ func (e *entry) deliver(response chan<- result) {
和基于互斥量的版本类似第一个对某个key的请求需要负责去调用函数f并传入这个key将结果存在条目里并关闭ready channel来广播条目的ready消息。使用`(*entry).call`来完成上述工作。
紧接着对同一个key的请求会发现map中已经有了存在的条目然后会等待结果变为ready并将结果从response发送给客户端的goroutien。上述工作是用`(*entry).deliver`来完成的。对call和deliver方法的调用必须让它们在自己的goroutine中进行以确保monitor goroutines不会因此而被阻塞住而没法处理新的请求。
紧接着对同一个key的请求会发现map中已经有了存在的条目然后会等待结果变为ready并将结果从response发送给客户端的goroutine。上述工作是用`(*entry).deliver`来完成的。对call和deliver方法的调用必须让它们在自己的goroutine中进行以确保monitor goroutines不会因此而被阻塞住而没法处理新的请求。
这个例子说明我们无论用上锁,还是通信来建立并发程序都是可行的。

View File

@@ -8,7 +8,7 @@
相反一个goroutine会以一个很小的栈开始其生命周期一般只需要2KB。一个goroutine的栈和操作系统线程一样会保存其活跃或挂起的函数调用的本地变量但是和OS线程不太一样的是一个goroutine的栈大小并不是固定的栈的大小会根据需要动态地伸缩。而goroutine的栈的最大值有1GB比传统的固定大小的线程栈要大得多尽管一般情况下大多goroutine都不需要这么大的栈。
** 练习 9.4:** 创建一个流水线程序支持用channel连接任意数量的goroutine在跑爆内存之前可以创建多少流水线阶段一个变量通过整个流水线需要用多久这个练习题翻译不是很确定
**练习 9.4:** 创建一个流水线程序支持用channel连接任意数量的goroutine在跑爆内存之前可以创建多少流水线阶段一个变量通过整个流水线需要用多久这个练习题翻译不是很确定
### 9.8.2. Goroutine调度
@@ -18,7 +18,7 @@ Go的运行时包含了其自己的调度器这个调度器使用了一些技
和操作系统的线程调度不同的是Go调度器并不是用一个硬件定时器而是被Go语言“建筑”本身进行调度的。例如当一个goroutine调用了time.Sleep或者被channel调用或者mutex操作阻塞时调度器会使其进入休眠并开始执行另一个goroutine直到时机到了再去唤醒第一个goroutine。因为这种调度方式不需要进入内核的上下文所以重新调度一个goroutine比调度一个线程代价要低得多。
** 练习 9.5: ** 写一个有两个goroutine的程序两个goroutine会向两个无buffer channel反复地发送ping-pong消息。这样的程序每秒可以支持多少次通信
**练习 9.5:** 写一个有两个goroutine的程序两个goroutine会向两个无buffer channel反复地发送ping-pong消息。这样的程序每秒可以支持多少次通信
### 9.8.3. GOMAXPROCS
@@ -42,7 +42,7 @@ $ GOMAXPROCS=2 go run hacker-cliché.go
在第一次执行时最多同时只能有一个goroutine被执行。初始情况下只有main goroutine被执行所以会打印很多1。过了一段时间后GO调度器会将其置为休眠并唤醒另一个goroutine这时候就开始打印很多0了在打印的时候goroutine是被调度到操作系统线程上的。在第二次执行时我们使用了两个操作系统线程所以两个goroutine可以一起被执行以同样的频率交替打印0和1。我们必须强调的是goroutine的调度是受很多因子影响的而runtime也是在不断地发展演进的所以这里的你实际得到的结果可能会因为版本的不同而与我们运行的结果有所不同。
** 练习9.6:** 测试一下计算密集型的并发程序练习8.5那样的会被GOMAXPROCS怎样影响到。在你的电脑上最佳的值是多少你的电脑CPU有多少个核心
**练习9.6:** 测试一下计算密集型的并发程序练习8.5那样的会被GOMAXPROCS怎样影响到。在你的电脑上最佳的值是多少你的电脑CPU有多少个核心
### 9.8.4. Goroutine没有ID号

View File

@@ -1,6 +1,6 @@
# Go语言圣经中文版
Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本仅供学习交流之用。对于希望学习CGO、Go汇编语言等高级用法的同学我们推荐[《Go语言高级编程》](https://github.com/chai2010/advanced-go-programming-book)开源图书。如果希望深入学习Go语言语法树结构可以参考[《Go语法树入门——开启自制编程语言和编译器之旅》](https://github.com/chai2010/go-ast-book)。如果想从头实现一个玩具Go语言可以参考[《从头实现µGo语言》](https://github.com/chai2010/ugo-compiler-book)。
Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本仅供学习交流之用。对于希望学习CGO、Go汇编语言等高级用法的同学我们推荐[《Go语言高级编程》](https://github.com/chai2010/advanced-go-programming-book)开源图书。如果希望深入学习Go语言语法树结构可以参考[《Go语法树入门——开启自制编程语言和编译器之旅》](https://github.com/chai2010/go-ast-book)。如果想从头实现一个玩具Go语言可以参考[《从头实现µGo语言》](https://github.com/wa-lang/ugo-compiler-book)µGo 是 [凹语言](https://wa-lang.org/) 阶段的产物)
![](cover.jpg)

File diff suppressed because one or more lines are too long

View File

@@ -1,146 +0,0 @@
// https://giscus.app
const data_repo = "gopl-zh/gopl-zh.github.com";
const data_repo_id = "MDEwOlJlcG9zaXRvcnk2MTUzMTQ2Mw==";
const data_category = "General";
const data_category_id = "DIC_kwDOA6rlR84CQnJW";
var initAll = function () {
var path = window.location.pathname;
if (path.endsWith("/print.html")) {
return;
}
var images = document.querySelectorAll("main img")
Array.prototype.forEach.call(images, function (img) {
img.addEventListener("click", function () {
BigPicture({
el: img,
});
});
});
// Un-active everything when you click it
Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) {
el.addEventHandler("click", function () {
Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) {
el.classList.remove("active");
});
el.classList.add("active");
});
});
var updateFunction = function () {
var id = null;
var elements = document.getElementsByClassName("header");
Array.prototype.forEach.call(elements, function (el) {
if (window.pageYOffset >= el.offsetTop) {
id = el;
}
});
Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) {
el.classList.remove("active");
});
Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) {
if (id == null) {
return;
}
if (id.href.localeCompare(el.href) == 0) {
el.classList.add("active");
}
});
};
var pagetoc = document.getElementsByClassName("pagetoc")[0];
var elements = document.getElementsByClassName("header");
Array.prototype.forEach.call(elements, function (el) {
var link = document.createElement("a");
// Indent shows hierarchy
var indent = "";
switch (el.parentElement.tagName) {
case "H1":
return;
case "H3":
indent = "20px";
break;
case "H4":
indent = "40px";
break;
default:
break;
}
link.appendChild(document.createTextNode(el.text));
link.style.paddingLeft = indent;
link.href = el.href;
pagetoc.appendChild(link);
});
updateFunction.call();
// Handle active elements on scroll
window.addEventListener("scroll", updateFunction);
document.getElementById("theme-list").addEventListener("click", function (e) {
var iframe = document.querySelector('.giscus-frame');
if (!iframe) return;
var theme;
if (e.target.className === "theme") {
theme = e.target.id;
} else {
return;
}
// 若当前 mdbook 主题不是 Light 或 Rust ,则将 giscuz 主题设置为 transparent_dark
var giscusTheme = "light"
if (theme != "light" && theme != "rust") {
giscusTheme = "transparent_dark";
}
var msg = {
setConfig: {
theme: giscusTheme
}
};
iframe.contentWindow.postMessage({ giscus: msg }, 'https://giscus.app');
});
pagePath = pagePath.replace("index.md", "");
pagePath = pagePath.replace(".md", "");
if (pagePath.length > 0) {
if (pagePath.charAt(pagePath.length-1) == "/"){
pagePath = pagePath.substring(0, pagePath.length-1);
}
}else {
pagePath = "index";
}
var giscusTheme = "light";
const themeClass = document.getElementsByTagName("html")[0].className;
if (themeClass.indexOf("light") == -1 && themeClass.indexOf("rust") == -1) {
giscusTheme = "transparent_dark";
}
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://giscus.app/client.js";
script.async = true;
script.crossOrigin = "anonymous";
script.setAttribute("data-repo", data_repo);
script.setAttribute("data-repo-id", data_repo_id);
script.setAttribute("data-category", data_category);
script.setAttribute("data-category-id", data_category_id);
script.setAttribute("data-mapping", "specific");
script.setAttribute("data-term", pagePath);
script.setAttribute("data-reactions-enabled", "1");
script.setAttribute("data-emit-metadata", "0");
script.setAttribute("data-input-position", "top");
script.setAttribute("data-theme", giscusTheme);
script.setAttribute("data-lang", "zh-CN");
script.setAttribute("data-loading", "lazy");
document.getElementById("giscus-container").appendChild(script);
};
window.addEventListener('load', initAll);

19
preface-zh.md Normal file
View File

@@ -0,0 +1,19 @@
# 译者序
在上个世纪70年代贝尔实验室的 [Ken Thompson](http://genius.cat-v.org/ken-thompson/) 和 [Dennis M. Ritchie](http://genius.cat-v.org/dennis-ritchie/) 合作发明了 [UNIX](http://doc.cat-v.org/unix/) 操作系统,同时 [Dennis M. Ritchie](http://genius.cat-v.org/dennis-ritchie/) 为了解决 [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](http://doc.cat-v.org/unix/) 系统,两大移动平台的操作系统 iOS 和 Android 也都是源于 [UNIX](http://doc.cat-v.org/unix/) 系统。C 系家族的编程语言占据统治地位达几十年之久)。在 [UNIX](http://doc.cat-v.org/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)
从早期提交日志中也可以看出Go 语言是从 [Ken Thompson](http://genius.cat-v.org/ken-thompson/) 发明的 B 语言、[Dennis M. Ritchie](http://genius.cat-v.org/dennis-ritchie/) 发明的 C 语言逐步演化过来的,是 C 语言家族的成员,因此很多人将 Go 语言称为 21 世纪的 C 语言。纵观这几年来的发展趋势Go 语言已经成为云计算、云存储时代最重要的基础编程语言。
在 C 语言发明之后约5年的时间之后1978年[Brian W. Kernighan](http://www.cs.princeton.edu/~bwk/) 和 [Dennis M. Ritchie](http://genius.cat-v.org/dennis-ritchie/) 合作编写出版了C语言方面的经典教材《[The C Programming Language](http://s3-us-west-2.amazonaws.com/belllabs-microsite-dritchie/cbook/index.html)》,该书被誉为 C 语言程序员的圣经,作者也被大家亲切地称为 [K&R](https://en.wikipedia.org/wiki/K%26R)。同样在 Go 语言正式发布2009 年)约 5 年之后2014 年开始写作2015 年出版),由 Go 语言核心团队成员 [Alan A. A. Donovan](https://github.com/adonovan) 和 [K&R](https://en.wikipedia.org/wiki/K%26R) 中的 [Brian W. Kernighan](http://www.cs.princeton.edu/~bwk/) 合作编写了Go语言方面的经典教材《[The Go Programming Language](http://gopl.io)》。Go 语言被誉为 21 世纪的 C 语言,如果说 [K&R](https://en.wikipedia.org/wiki/K%26R) 所著的是圣经的旧约,那么 D&K 所著的必将成为圣经的新约。该书介绍了 Go 语言几乎全部特性,并且随着语言的深入层层递进,对每个细节都解读得非常细致,每一节内容都精彩不容错过,是广大 Gopher 的必读书目。大部分 Go 语言核心团队的成员都参与了该书校对工作,因此该书的质量是可以完全放心的。
同时,单凭阅读和学习其语法结构并不能真正地掌握一门编程语言,必须进行足够多的编程实践——亲自编写一些程序并研究学习别人写的程序。要从利用 Go 语言良好的特性使得程序模块化,充分利用 Go 的标准函数库以 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/)提交。
最后希望这本书能够帮助大家用Go语言快乐地编程。
2016年 1月 于 武汉

View File

@@ -6,7 +6,7 @@
下图展示了有哪些早期的编程语言对Go语言的设计产生了重要影响。
![](../images/ch0-01.png)
![](./images/ch0-01.png)
Go语言有时候被描述为“类C语言”或者是“21世纪的C语言”。Go从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想还有C语言一直所看中的编译后机器码的运行效率以及和现有操作系统的无缝适配。

View File

@@ -1,54 +0,0 @@
@media only screen and (max-width:1079px) {
.sidetoc {
display: none;
}
}
@media only screen and (min-width:1080px) {
main {
position: relative;
padding-right: 170px;
}
.sidetoc {
margin-left: auto;
margin-right: auto;
/*left: calc(100% + (var(--content-max-width))/4 - 180px);*/
left: calc(100% - 200px);
position: absolute;
}
.pagetoc {
position: fixed;
width: 200px;
height: calc(100vh - var(--menu-bar-height) - 0.67em * 4);
overflow: auto;
/* z-index: 1000; */
}
.pagetoc a {
border-left: 1px solid var(--sidebar-bg);
color: var(--fg) !important;
display: block;
padding-bottom: 5px;
padding-top: 5px;
padding-left: 10px;
text-align: left;
text-decoration: none;
font-size: 1.2rem;
}
.pagetoc a:hover,
.pagetoc a.active {
background: var(--sidebar-bg);
color: var(--sidebar-fg) !important;
}
.pagetoc .active {
background: var(--sidebar-bg);
color: var(--sidebar-fg);
}
}
.page-footer {
margin-top: 50px;
border-top: 1px solid #ccc;
overflow: hidden;
padding: 10px 0;
color: gray;
}

View File

@@ -1,345 +0,0 @@
<!DOCTYPE HTML>
<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>{{ title }}</title>
{{#if is_print }}
<meta name="robots" content="noindex" />
{{/if}}
{{#if base_url}}
<base href="{{ base_url }}">
{{/if}}
<!-- Custom HTML head -->
{{> head}}
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="{{ description }}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
{{#if favicon_svg}}
<link rel="icon" href="{{ path_to_root }}favicon.svg">
{{/if}}
{{#if favicon_png}}
<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
{{/if}}
<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
{{#if print_enable}}
<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
{{/if}}
<!-- Fonts -->
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
{{#if copy_fonts}}
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
{{/if}}
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="{{ path_to_root }}highlight.css">
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
<!-- Custom theme stylesheets -->
{{#each additional_css}}
<link rel="stylesheet" href="{{ ../path_to_root }}{{ this }}">
{{/each}}
{{#if mathjax_support}}
<!-- MathJax -->
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
{{/if}}
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "{{ path_to_root }}";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('{{ default_theme }}')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
{{#toc}}{{/toc}}
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
{{> header}}
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">{{ theme_option "Light" }}</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">{{ theme_option "Rust" }}</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">{{ theme_option "Coal" }}</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">{{ theme_option "Navy" }}</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">{{ theme_option "Ayu" }}</button></li>
</ul>
{{#if search_enabled}}
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
{{/if}}
</div>
<h1 class="menu-title">{{ book_title }}</h1>
<div class="right-buttons">
{{#if print_enable}}
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
{{/if}}
{{#if git_repository_url}}
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
</a>
{{/if}}
{{#if git_repository_edit_url}}
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
{{/if}}
</div>
</div>
{{#if search_enabled}}
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
{{/if}}
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<!-- Page table of contents -->
<div class="sidetoc"><nav class="pagetoc"></nav></div>
<main>
<!-- 头部 -->
<ul dir="auto">
<li><em>KusonStack一站式可编程配置技术栈(Go): <a href="https://github.com/KusionStack/kusion">https://github.com/KusionStack/kusion</a></em></li>
<li><em>KCL 配置编程语言(Rust): <a href="https://github.com/KusionStack/KCLVM">https://github.com/KusionStack/KCLVM</a></em></li>
<li><em>凹语言™: <a href="https://github.com/wa-lang/wa">https://github.com/wa-lang/wa</a></em></li>
</ul>
<hr>
{{{ content }}}
<!-- 公众号 -->
<hr>
<table>
<tr>
<td>
<img width="222px" src="https://chai2010.cn/advanced-go-programming-book/css.png">
</td>
<td>
<img width="222px" src="https://chai2010.cn/advanced-go-programming-book/cch.png">
</td>
</tr>
</table>
<div id="giscus-container"></div>
<footer class="page-footer">
<span>© 2015-2016 | <a href="https://github.com/gopl-zh"> Go语言圣经中文版</a>, 仅学习交流使用</span>
</footer>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
{{#previous}}
<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
{{/previous}}
{{#next}}
<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
{{/next}}
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
{{#previous}}
<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
{{/previous}}
{{#next}}
<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
{{/next}}
</nav>
</div>
{{#if livereload}}
<!-- Livereload script (if served using the cli tool) -->
<script type="text/javascript">
var socket = new WebSocket("{{{livereload}}}");
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload();
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
{{/if}}
{{#if google_analytics}}
<!-- Google Analytics Tag -->
<script type="text/javascript">
var localAddrs = ["localhost", "127.0.0.1", ""];
// make sure we don't activate google analytics if the developer is
// inspecting the book locally...
if (localAddrs.indexOf(document.location.hostname) === -1) {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', '{{google_analytics}}', 'auto');
ga('send', 'pageview');
}
</script>
{{/if}}
{{#if playground_line_numbers}}
<script type="text/javascript">
window.playground_line_numbers = true;
</script>
{{/if}}
{{#if playground_copyable}}
<script type="text/javascript">
window.playground_copyable = true;
</script>
{{/if}}
{{#if playground_js}}
<script src="{{ path_to_root }}ace.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}editor.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}mode-rust.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}theme-dawn.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
{{/if}}
{{#if search_js}}
<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
{{/if}}
<script src="{{ path_to_root }}clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
var pagePath = "{{ path }}"
</script>
<!-- Custom JS scripts -->
{{#each additional_js}}
<script type="text/javascript" src="{{ ../path_to_root }}{{this}}"></script>
{{/each}}
{{#if is_print}}
{{#if mathjax_support}}
<script type="text/javascript">
window.addEventListener('load', function() {
MathJax.Hub.Register.StartupHook('End', function() {
window.setTimeout(window.print, 100);
});
});
</script>
{{else}}
<script type="text/javascript">
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
{{/if}}
{{/if}}
</body>
</html>