From 57e670858308f7bb4dc807a3114e940eb18699e1 Mon Sep 17 00:00:00 2001 From: barceshow Date: Fri, 15 Jan 2016 20:52:53 +0800 Subject: [PATCH 01/23] Update 15.1.md --- eBook/15.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/15.1.md b/eBook/15.1.md index c9354cd..e89804b 100644 --- a/eBook/15.1.md +++ b/eBook/15.1.md @@ -1,6 +1,6 @@ # 15.1 tcp服务器 -这部分我们将使用TCP协议和在14章讲到的携程范式编写一个简单的客户端-服务器应用,一个(web)服务器应用需要响应众多客户端的并发请求:go会为每一个客户端产生一个携程用来处理请求。我们需要使用net包中网络通信的功能。它包含了用于TCP/IP以及UDP协议、域名解析等方法。 +这部分我们将使用TCP协议和在14章讲到的协程范式编写一个简单的客户端-服务器应用,一个(web)服务器应用需要响应众多客户端的并发请求:go会为每一个客户端产生一个协程用来处理请求。我们需要使用net包中网络通信的功能。它包含了用于TCP/IP以及UDP协议、域名解析等方法。 服务器代码,单独的一个文件: From b4a0303be7b89467004d576798352271e23dbb57 Mon Sep 17 00:00:00 2001 From: mengskysama Date: Sat, 16 Jan 2016 00:27:25 +0800 Subject: [PATCH 02/23] Update 09.2.md MatchString --- eBook/09.2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/09.2.md b/eBook/09.2.md index 88d4c7f..7400f7a 100644 --- a/eBook/09.2.md +++ b/eBook/09.2.md @@ -13,7 +13,7 @@ ok, _ := regexp.Match(pat, []byte(searchIn)) 变量 ok 将返回 true 或者 false,我们也可以使用 `MatchString`: ```go -ok, _ := regexp.MathString(pat, searchIn) +ok, _ := regexp.MatchString(pat, searchIn) ``` 更多方法中,必须先将正则通过 `Compile` 方法返回一个 Regexp 对象。然后我们将掌握一些匹配,查找,替换相关的功能。 From 78fd378f1a1a49cc7518b69da0b99199734e4ff5 Mon Sep 17 00:00:00 2001 From: mengskysama Date: Sat, 16 Jan 2016 14:58:38 +0800 Subject: [PATCH 03/23] Update 10.6.md --- eBook/10.6.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/10.6.md b/eBook/10.6.md index 18f300e..84bc321 100644 --- a/eBook/10.6.md +++ b/eBook/10.6.md @@ -292,7 +292,7 @@ func main() { ## 10.6.4 方法和未导出字段 -考虑 `person2.go` 中的 `person` 包:类型 `Person` 被明确的导出了,但是它的字段没有被导出。例如在 `use_person2.go` 中 `p.firsetname` 就是错误的。该如何在另一个程序中修改或者只是读取一个 `Person` 的名字呢? +考虑 `person2.go` 中的 `person` 包:类型 `Person` 被明确的导出了,但是它的字段没有被导出。例如在 `use_person2.go` 中 `p.firstName` 就是错误的。该如何在另一个程序中修改或者只是读取一个 `Person` 的名字呢? 这可以通过面向对象语言一个众所周知的技术来完成:提供 getter 和 setter 方法。对于 setter 方法使用 Set 前缀,对于 getter 方法只适用成员名。 From a7cc4c395edaf64e54c78ed598d024c6db1ae024 Mon Sep 17 00:00:00 2001 From: smart Date: Sun, 17 Jan 2016 10:50:24 +0800 Subject: [PATCH 04/23] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=8B=BC=E5=86=99?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第60行 --- eBook/09.11.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/09.11.md b/eBook/09.11.md index 42a03d3..86cfbd8 100644 --- a/eBook/09.11.md +++ b/eBook/09.11.md @@ -1 +1 @@ -# 9.11 在 Go 程序中使用外部库 (本节我们将创建一个 Web 应用和它的 Google App Engine 版本,在第 19 和 21 章分别说明,当你阅读到这些章节时可以再回到这个例子。) 当开始一个新项目或增加新的功能到现有的项目,你可以通过在应用程序中使用已经存在的库来节省开发时间。为了做到这一点,你必须理解库的 API(应用编程接口),那就是:库中有哪些方法可以调用,如何调用。你可能没有这个库的源代码,但作者肯定有记载的 API 以及详细介绍了如何使用它。 作为一个例子,我们将使用谷歌的 API 的 urlshortener 编写一个小程序:你可以尝试一下在 http://goo.gl/ 输入一个像 "http://www.destandaard.be" 这样的URL,你会看到一个像 "http://goo.gl/O9SUO" 这样更短的 URL 返回,也就是说,在 Twitter 之类的服务中这是非常容易嵌入的。谷歌 urlshortener 服务的文档可以在 "http://code.google.com/apis/urlshortener/" 找到。(第 19 章,我们将开发自己版本的 urlshortener)。 谷歌将这项技术提供给其他开发者,作为 API 我们可以在我们自己的应用程序中调用(释放到指定的限制。他们也生成了一个 Go 语言客户端库使其变得更容易。 备注:谷歌让通过使用 Google API Go 客户端服务的开发者生活变得更简单,Go 客户端程序自动生成于 Google 库的 JSON 描述。更多详情在 [项目页面](http://code.google.com/p/google-api-go-client/) 查看。 下载并安装 Go 客户端库: 将通过 go install 实现。但是首先要验证环境变量中是否含有 `GOPATH` 变量,因为外部源码将被下载到 `$GOPATH/src` 目录下并被安装到 `$GOPATH/PKG/"machine_arch"/` 目录下。 我们将通过在终端调用以下命令来安装 API: go install google-api-go-client.google.com/hg/urlshortener/v1 go install 将下载源码,编译并安装包 使用 urlshortener 服务的 web 程序: 现在我们可以通过导入并赋予别名来使用已安装的包: `import urlshortener "google-api-go-client.googlecode.com/hg/urlshortener/v1"` 现在我们写一个 Web 应用(参见第 15 章 4-8 节)通过表单实现短地址和长地址的相互转换。我们将使用 `template` 包并写三个处理函数:root 函数通过执行表单模板来展示表单。short 函数将长地址转换为短地址,long 函数逆向转换。 要调用 urlshortener 接口必须先通过 http 包中的默认客户端创建一个服务实例 urlshortenerSvc: ```go urlshortenerSvc, _ := urlshortener.New(http.DefaultClient) ``` 我们通过调用服务中的 `Url.Insert` 中的 `Do` 方法传入包含长地址的 `Url` 数据结构从而获取短地址: ```go url, _ := urlshortenerSvc.Url.Insert(&urlshortener.Url{LongUrl: longUrl}).Do() ``` 返回 `url` 的 `Id` 便是我们需要的短地址。 我们通过调用服务中的 `Url.Get` 中的 `Do` 方法传入包含短地址的Url数据结构从而获取长地址: ```go url, error := urlshortenerSvc.Url.Get(shwortUrl).Do() ``` 返回的长地址便是转换前的原始地址。 示例 9.9 [urlshortener.go](examples/chapter_9/urlshortener.go) ```go package main import ( "fmt" "net/http" "text/template" rlshortener "google-api-go-client.googlecode.com/hg/urlshortener/v1" ) func main() { http.HandleFunc("/", root) http.HandleFunc("/short", short) http.HandleFunc("/long", long) http.ListenAndServe("localhost:8080", nil) } // the template used to show the forms and the results web page to the user var rootHtmlTmpl = template.Must(template.New("rootHtml").Parse(`

URL SHORTENER

{{if .}}{{.}}

{{end}}
Shorten this:

Expand this: http://goo.gl/
`)) func root(w http.ResponseWriter, r *http.Request) { rootHtmlTmpl.Execute(w, nil) } func short(w http.ResponseWriter, r *http.Request) { longUrl := r.FormValue("longUrl") urlshortenerSvc, _ := urlshortener.New(http.DefaultClient) url, _ := urlshortenerSvc.Url.Insert(&urlshortener.Url{LongUrl: longUrl,}).Do() rootHtmlTmpl.Execute(w, fmt.Sprintf("Shortened version of %s is : %s", longUrl, url.Id)) } func long(w http.ResponseWriter, r *http.Request) { shortUrl := "http://goo.gl/" + r.FormValue("shortUrl") urlshortenerSvc, _ := urlshortener.New(http.DefaultClient) url, err := urlshortenerSvc.Url.Get(shortUrl).Do() if err != nil { fmt.Println("error: %v", err) return } rootHtmlTmpl.Execute(w, fmt.Sprintf("Longer version of %s is : %s", shortUrl, url.LongUrl)) } ``` 执行这段代码: go run urlshortener.go 通过浏览 `http://localhost:8080/` 的页面来测试。 为了代码的简洁我们并没有检测返回的错误状态,但是在真实的生产环境的应用中一定要做检测。 将应用放入Google App Engine,我们只需要在之前的代码中作出如下改变: package main -> package urlshort func main() -> func init() 创建一个和包同名的目录 `urlshort`,并将以下两个安装目录复制到这个目录: google-api-go-client.googlecode.com/hg/urlshortener google-api-go-client.googlecode.com/hg/google-api 此外还要配置下配置文件 `app.yaml`,内容如下: ```yaml application: urlshort version: 0-1-test runtime: go api_version: 3 handlers: - url: /.* script: _go_app ``` 现在你可以去到你的项目目录并在终端运行:`dev_appserver.py urlshort` 在浏览器打开你的 Web应用:http://localhost:8080。 ## 链接 - [目录](directory.md) - 上一节:[Go 的外部包和项目](09.10.md) - 下一章:[结构(struct)与方法(method)](10.0.md) \ No newline at end of file +# 9.11 在 Go 程序中使用外部库 (本节我们将创建一个 Web 应用和它的 Google App Engine 版本,在第 19 和 21 章分别说明,当你阅读到这些章节时可以再回到这个例子。) 当开始一个新项目或增加新的功能到现有的项目,你可以通过在应用程序中使用已经存在的库来节省开发时间。为了做到这一点,你必须理解库的 API(应用编程接口),那就是:库中有哪些方法可以调用,如何调用。你可能没有这个库的源代码,但作者肯定有记载的 API 以及详细介绍了如何使用它。 作为一个例子,我们将使用谷歌的 API 的 urlshortener 编写一个小程序:你可以尝试一下在 http://goo.gl/ 输入一个像 "http://www.destandaard.be" 这样的URL,你会看到一个像 "http://goo.gl/O9SUO" 这样更短的 URL 返回,也就是说,在 Twitter 之类的服务中这是非常容易嵌入的。谷歌 urlshortener 服务的文档可以在 "http://code.google.com/apis/urlshortener/" 找到。(第 19 章,我们将开发自己版本的 urlshortener)。 谷歌将这项技术提供给其他开发者,作为 API 我们可以在我们自己的应用程序中调用(释放到指定的限制。他们也生成了一个 Go 语言客户端库使其变得更容易。 备注:谷歌让通过使用 Google API Go 客户端服务的开发者生活变得更简单,Go 客户端程序自动生成于 Google 库的 JSON 描述。更多详情在 [项目页面](http://code.google.com/p/google-api-go-client/) 查看。 下载并安装 Go 客户端库: 将通过 go install 实现。但是首先要验证环境变量中是否含有 `GOPATH` 变量,因为外部源码将被下载到 `$GOPATH/src` 目录下并被安装到 `$GOPATH/PKG/"machine_arch"/` 目录下。 我们将通过在终端调用以下命令来安装 API: go install google-api-go-client.google.com/hg/urlshortener/v1 go install 将下载源码,编译并安装包 使用 urlshortener 服务的 web 程序: 现在我们可以通过导入并赋予别名来使用已安装的包: `import urlshortener "google-api-go-client.googlecode.com/hg/urlshortener/v1"` 现在我们写一个 Web 应用(参见第 15 章 4-8 节)通过表单实现短地址和长地址的相互转换。我们将使用 `template` 包并写三个处理函数:root 函数通过执行表单模板来展示表单。short 函数将长地址转换为短地址,long 函数逆向转换。 要调用 urlshortener 接口必须先通过 http 包中的默认客户端创建一个服务实例 urlshortenerSvc: ```go urlshortenerSvc, _ := urlshortener.New(http.DefaultClient) ``` 我们通过调用服务中的 `Url.Insert` 中的 `Do` 方法传入包含长地址的 `Url` 数据结构从而获取短地址: ```go url, _ := urlshortenerSvc.Url.Insert(&urlshortener.Url{LongUrl: longUrl}).Do() ``` 返回 `url` 的 `Id` 便是我们需要的短地址。 我们通过调用服务中的 `Url.Get` 中的 `Do` 方法传入包含短地址的Url数据结构从而获取长地址: ```go url, error := urlshortenerSvc.Url.Get(shwortUrl).Do() ``` 返回的长地址便是转换前的原始地址。 示例 9.9 [urlshortener.go](examples/chapter_9/urlshortener.go) ```go package main import ( "fmt" "net/http" "text/template" urlshortener "google-api-go-client.googlecode.com/hg/urlshortener/v1" ) func main() { http.HandleFunc("/", root) http.HandleFunc("/short", short) http.HandleFunc("/long", long) http.ListenAndServe("localhost:8080", nil) } // the template used to show the forms and the results web page to the user var rootHtmlTmpl = template.Must(template.New("rootHtml").Parse(`

URL SHORTENER

{{if .}}{{.}}

{{end}}
Shorten this:

Expand this: http://goo.gl/
`)) func root(w http.ResponseWriter, r *http.Request) { rootHtmlTmpl.Execute(w, nil) } func short(w http.ResponseWriter, r *http.Request) { longUrl := r.FormValue("longUrl") urlshortenerSvc, _ := urlshortener.New(http.DefaultClient) url, _ := urlshortenerSvc.Url.Insert(&urlshortener.Url{LongUrl: longUrl,}).Do() rootHtmlTmpl.Execute(w, fmt.Sprintf("Shortened version of %s is : %s", longUrl, url.Id)) } func long(w http.ResponseWriter, r *http.Request) { shortUrl := "http://goo.gl/" + r.FormValue("shortUrl") urlshortenerSvc, _ := urlshortener.New(http.DefaultClient) url, err := urlshortenerSvc.Url.Get(shortUrl).Do() if err != nil { fmt.Println("error: %v", err) return } rootHtmlTmpl.Execute(w, fmt.Sprintf("Longer version of %s is : %s", shortUrl, url.LongUrl)) } ``` 执行这段代码: go run urlshortener.go 通过浏览 `http://localhost:8080/` 的页面来测试。 为了代码的简洁我们并没有检测返回的错误状态,但是在真实的生产环境的应用中一定要做检测。 将应用放入Google App Engine,我们只需要在之前的代码中作出如下改变: package main -> package urlshort func main() -> func init() 创建一个和包同名的目录 `urlshort`,并将以下两个安装目录复制到这个目录: google-api-go-client.googlecode.com/hg/urlshortener google-api-go-client.googlecode.com/hg/google-api 此外还要配置下配置文件 `app.yaml`,内容如下: ```yaml application: urlshort version: 0-1-test runtime: go api_version: 3 handlers: - url: /.* script: _go_app ``` 现在你可以去到你的项目目录并在终端运行:`dev_appserver.py urlshort` 在浏览器打开你的 Web应用:http://localhost:8080。 ## 链接 - [目录](directory.md) - 上一节:[Go 的外部包和项目](09.10.md) - 下一章:[结构(struct)与方法(method)](10.0.md) \ No newline at end of file From f7687ba85101bc082623a18b809a1f705a94435a Mon Sep 17 00:00:00 2001 From: smart Date: Mon, 18 Jan 2016 10:20:37 +0800 Subject: [PATCH 05/23] update --- eBook/08.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/08.1.md b/eBook/08.1.md index edcc530..e170697 100644 --- a/eBook/08.1.md +++ b/eBook/08.1.md @@ -9,7 +9,7 @@ var map1 map[keytype]valuetype var map1 map[string]int ``` -(`[keytype]`` 和 `valuetype` 之间允许有空格,但是 gofmt 移除了空格) +(`[keytype]` 和 `valuetype` 之间允许有空格,但是 gofmt 移除了空格) 在声明的时候不需要知道 map 的长度,map 是可以动态增长的。 From 9bb931257a88a58517ed5a0e3a559d4915fe8be8 Mon Sep 17 00:00:00 2001 From: songleo Date: Mon, 18 Jan 2016 16:34:44 +0800 Subject: [PATCH 06/23] review --- eBook/10.6.md | 4 ++-- eBook/10.7.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eBook/10.6.md b/eBook/10.6.md index 18f300e..4c7565b 100644 --- a/eBook/10.6.md +++ b/eBook/10.6.md @@ -139,7 +139,7 @@ func (t time.Time) first3Chars() string { } ``` -类型在在其他的,或是非本地的包里定义,在它上面定义方法都会得到和上面同样的错误。 +类型在其他的,或是非本地的包里定义,在它上面定义方法都会得到和上面同样的错误。 但是有一个间接的方式:可以先定义该类型(比如:int 或 float)的别名类型,然后再为别名类型定义方法。或者像下面这样将它作为匿名类型嵌入在一个新的结构体中。当然方法只在这个别名类型上有效。 @@ -198,7 +198,7 @@ First 3 chars: Mon 如果想要方法改变接收者的数据,就在接收者的指针类型上定义该方法。否则,就在普通的值类型上定义方法。 -下面的例子 `pointer_value.go` 作了说明:`change()`接受一个指向 B 的指针,并改变它内部的成员;`write()` 接受通过拷贝接受 B 的值并只输出B的内容。注意 Go 为我们做了探测工作,我们自己并没有指出是是否在指针上调用方法,Go 替我们做了这些事情。b1 是值而 b2 是指针,方法都支持运行了。 +下面的例子 `pointer_value.go` 作了说明:`change()`接受一个指向 B 的指针,并改变它内部的成员;`write()` 接受通过拷贝接受 B 的值并只输出B的内容。注意 Go 为我们做了探测工作,我们自己并没有指出是否在指针上调用方法,Go 替我们做了这些事情。b1 是值而 b2 是指针,方法都支持运行了。 示例 10.13 pointer_value.go: diff --git a/eBook/10.7.md b/eBook/10.7.md index 82bba86..19889e0 100644 --- a/eBook/10.7.md +++ b/eBook/10.7.md @@ -1,6 +1,6 @@ # 10.7 类型的 String() 方法和格式化描述符 -当定义一个了有很多方法的类型时,十之八九你会使用 `String()` 方法来定制类型的字符串形式的输出,换句话说:一种可阅读性和打印性的输出。如果类型定义了 `String()` 方法,它会被用在 `fmt.Printf()` 中生成默认的输出:等同于使用格式化描述符 `%v` 产生的输出。还有 `fmt.Print()` 和 `fmt.Println()` 也会自动使用 `String()` 方法。 +当定义了一个有很多方法的类型时,十之八九你会使用 `String()` 方法来定制类型的字符串形式的输出,换句话说:一种可阅读性和打印性的输出。如果类型定义了 `String()` 方法,它会被用在 `fmt.Printf()` 中生成默认的输出:等同于使用格式化描述符 `%v` 产生的输出。还有 `fmt.Print()` 和 `fmt.Println()` 也会自动使用 `String()` 方法。 我们使用第 10.4 节中程序的类型来进行测试: From 672e8cd7c9c09d5c4b0a08652eb7e5a667eb66b4 Mon Sep 17 00:00:00 2001 From: smart Date: Wed, 20 Jan 2016 16:10:34 +0800 Subject: [PATCH 07/23] fix 10.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 结构体的初始化错误 --- eBook/10.1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eBook/10.1.md b/eBook/10.1.md index 09b9e50..ee97b2d 100644 --- a/eBook/10.1.md +++ b/eBook/10.1.md @@ -95,8 +95,8 @@ p.i 或者: ```go - var mt struct1 - ms := struct1{10, 15.5, "Chris"} + var ms struct1 + ms = struct1{10, 15.5, "Chris"} ``` 混合字面量语法(composite literal syntax)`&struct1{a, b, c}` 是一种简写,底层仍然会调用 `new ()`,这里值的顺序必须按照字段顺序来写。在下面的例子中能看到可以通过在值的前面放上字段名来初始化字段的方式。表达式 `new(Type)` 和 `&Type{}` 是等价的。 From d78be9ae6b4e7405d889e631900a4fa50ae30c54 Mon Sep 17 00:00:00 2001 From: smart Date: Wed, 20 Jan 2016 16:56:25 +0800 Subject: [PATCH 08/23] fix 10.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改结构体返回类型 --- eBook/10.2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/10.2.md b/eBook/10.2.md index 14047fa..2bce19b 100644 --- a/eBook/10.2.md +++ b/eBook/10.2.md @@ -19,7 +19,7 @@ func NewFile(fd int, name string) *File { return nil } - return &File(fd, name) + return &File{fd, name} } ``` From a0224fcdbe8919332bfcb2e72429d18c0c593262 Mon Sep 17 00:00:00 2001 From: smart Date: Wed, 20 Jan 2016 17:39:48 +0800 Subject: [PATCH 09/23] fix 10.5.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改打印函数 --- eBook/10.5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/10.5.md b/eBook/10.5.md index 27032d0..70022ce 100644 --- a/eBook/10.5.md +++ b/eBook/10.5.md @@ -43,7 +43,7 @@ func main() { // 使用结构体字面量 outer2 := outerS{6, 7.5, 60, innerS{5, 10}} - fmt.Printf("outer2 is:", outer2) + fmt.Println("outer2 is:", outer2) } ``` From 572ed6eeedd19ff61910e08de1f11f9b185b7545 Mon Sep 17 00:00:00 2001 From: smart Date: Thu, 21 Jan 2016 17:00:02 +0800 Subject: [PATCH 10/23] fix 10.6.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改语法错误 --- eBook/10.6.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eBook/10.6.md b/eBook/10.6.md index 18f300e..92275fc 100644 --- a/eBook/10.6.md +++ b/eBook/10.6.md @@ -238,9 +238,9 @@ func main() { 我们知道方法将指针作为接收者不是必须的,如下面的例子,我们只是需要 `Point3` 的值来做计算: ```go -type Point3 struct { x, y, z float } +type Point3 struct { x, y, z float64 } // A method on Point3 -func (p Point3) Abs float { +func (p Point3) Abs() float64 { return math.Sqrt(p.x*p.x + p.y*p.y + p.z*p.z) } ``` From c65641364ea1779e49671818c01154da16da676c Mon Sep 17 00:00:00 2001 From: smart Date: Fri, 22 Jan 2016 12:24:56 +0800 Subject: [PATCH 11/23] fix 14.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正拼写错误 --- eBook/14.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/14.1.md b/eBook/14.1.md index a88f517..624076d 100644 --- a/eBook/14.1.md +++ b/eBook/14.1.md @@ -12,7 +12,7 @@ 解决之道在于同步不同的线程,对数据加锁,这样同时就只有一个线程可以变更数据。在 Go 的标准库 `sync` 中有一些工具用来在低级别的代码中实现加锁;我们在第 [9.3](9.3.md) 节中讨论过这个问题。不过过去的软件开发经验告诉我们这会带来更高的复杂度,更容易使代码出错以及更低的性能,所以这个经典的方法明显不再适合现代多核/多处理器编程:`thread-per-connection` 模型不够有效。 -Go 更倾向于其他的方式,在诸多比较合适的范式中,有个被称作 `Communicating Sequential Processes(顺序通信处理)`(CSP, C. Hoare 发明的)还有一个叫做 `message passing-model(消息传递)`(已经运用在了其他语言中,比如 Eralng)。 +Go 更倾向于其他的方式,在诸多比较合适的范式中,有个被称作 `Communicating Sequential Processes(顺序通信处理)`(CSP, C. Hoare 发明的)还有一个叫做 `message passing-model(消息传递)`(已经运用在了其他语言中,比如 Erlang)。 在 Go 中,应用程序并发处理的部分被称作 `goroutines(协程)`,它可以进行更有效的并发运算。在协程和操作系统线程之间并无一对一的关系:协程是根据一个或多个线程的可用性,映射(多路复用,执行于)在他们之上的;协程调度器在 Go 运行时很好的完成了这个工作。 From 88713d19d9f53253ab2b419aa4371ec6b3260f61 Mon Sep 17 00:00:00 2001 From: smart Date: Sun, 24 Jan 2016 17:04:46 +0800 Subject: [PATCH 12/23] fix 14.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除多余的字 --- eBook/14.4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/14.4.md b/eBook/14.4.md index b426d7e..834973c 100644 --- a/eBook/14.4.md +++ b/eBook/14.4.md @@ -128,7 +128,7 @@ Received on channel 1: 94348 - `channel1` 用来接收极坐标 - `channel2` 用来接收笛卡尔坐标 -转换过程需要在协程中进行,从 channel1 中读取然后发哦送到 channel2。实际上做这种计算不提倡使用协程和通道,但是如果运算量很大很耗时,这种方案设计就非常合适了。 +转换过程需要在协程中进行,从 channel1 中读取然后发送到 channel2。实际上做这种计算不提倡使用协程和通道,但是如果运算量很大很耗时,这种方案设计就非常合适了。 练习 14.11: [concurrent_pi.go](exercises/chapter_14/concurrent_pi.go) / [concurrent_pi2.go](exercises/chapter_14/concurrent_pi2.go) From cc051ec10c621d57d8fe3bc4efb812c3dab94fd3 Mon Sep 17 00:00:00 2001 From: remones Date: Wed, 10 Feb 2016 12:41:28 +0800 Subject: [PATCH 13/23] =?UTF-8?q?fix=2014.1:=20=E4=BF=AE=E6=AD=A3=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=E4=B8=8D=E6=98=8E=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBook/14.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/14.1.md b/eBook/14.1.md index 624076d..4e63bd5 100644 --- a/eBook/14.1.md +++ b/eBook/14.1.md @@ -2,7 +2,7 @@ ## 14.1.1 什么是协程 -一个应用程序是运行在机器上的一个进程;进程是一个运行在自己内存地址空间里的独立执行体。一个进程由一个或多个操作系统线程组成,这些线程其实是共享同一个内存地址空间的一起工作的执行体。几乎所有'正式'的程序都是多线程的,以便让用户或计算机不必等待,或者能够同时服务多个请求(如 Web 服务器),或增加性能和吞吐量(例如,通过对不同的数据集并行执行代码)。一个并发程序可以在一个处理器或者内核上使用多个线程来执行任务,但是只有在同一个程序在某一个时间点在多个些处理内核或处理器上同时执行的任务才是真正的并行。 +一个应用程序是运行在机器上的一个进程;进程是一个运行在自己内存地址空间里的独立执行体。一个进程由一个或多个操作系统线程组成,这些线程其实是共享同一个内存地址空间的一起工作的执行体。几乎所有'正式'的程序都是多线程的,以便让用户或计算机不必等待,或者能够同时服务多个请求(如 Web 服务器),或增加性能和吞吐量(例如,通过对不同的数据集并行执行代码)。一个并发程序可以在一个处理器或者内核上使用多个线程来执行任务,但是只有同一个程序在某个时间点同时运行在多核或者多处理器上才是真正的并行。 并行是一种通过使用多处理器以提高速度的能力。所以并发程序可以是并行的,也可以不是。 From e63c2b15fc0ffdb768e5f9168efd39ba532f959a Mon Sep 17 00:00:00 2001 From: phddone Date: Wed, 24 Feb 2016 20:17:37 -0800 Subject: [PATCH 14/23] possible typo --- eBook/05.1.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/eBook/05.1.md b/eBook/05.1.md index a2d182c..b23eb44 100644 --- a/eBook/05.1.md +++ b/eBook/05.1.md @@ -193,8 +193,6 @@ func main() { 下面的代码片段展示了如何通过在初始化语句中获取函数 `process()` 的返回值,并在条件语句中作为判定条件来决定是否执行 if 结构中的代码: ```go -if value := process(data); value > max { - ... if value := process(data); value > max { ... } From 3aa05b82d314e66cf67210ec44e5060ace41c79a Mon Sep 17 00:00:00 2001 From: Yixin Luo <18810541851@163.com> Date: Mon, 29 Feb 2016 11:15:07 +0800 Subject: [PATCH 15/23] =?UTF-8?q?=E5=AE=9E=E4=BE=8B=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=B0=8F=E7=96=8F=E5=BF=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBook/11.10.md | 2 +- eBook/11.9.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eBook/11.10.md b/eBook/11.10.md index 5d27cde..b98c178 100644 --- a/eBook/11.10.md +++ b/eBook/11.10.md @@ -102,7 +102,7 @@ func main() { ``` type: float64 -value: +value: 3.4 type: float64 kind: float64 value: 3.4 diff --git a/eBook/11.9.md b/eBook/11.9.md index 7b4432b..1be83a9 100644 --- a/eBook/11.9.md +++ b/eBook/11.9.md @@ -1,4 +1,4 @@ -# 11.9 空接口 +# 11.9 空接口 ## 11.9.1 概念 @@ -164,7 +164,7 @@ var interfaceSlice []interface{} = dataSlice var dataSlice []myType = FuncReturnSlice() var interfaceSlice []interface{} = make([]interface{}, len(dataSlice)) for ix, d := range dataSlice { - interfaceSlice[i] = d + interfaceSlice[ix] = d } ``` From a29620b4467f2705692494bd5426869b56a75659 Mon Sep 17 00:00:00 2001 From: Yixin Luo <18810541851@163.com> Date: Mon, 29 Feb 2016 22:22:22 +0800 Subject: [PATCH 16/23] =?UTF-8?q?=E4=B8=80=E4=BA=9B=E5=B0=8F=E7=91=95?= =?UTF-8?q?=E7=96=B5=E7=9A=84=E6=9B=B4=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBook/12.2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eBook/12.2.md b/eBook/12.2.md index 7db9991..b4c7135 100644 --- a/eBook/12.2.md +++ b/eBook/12.2.md @@ -246,7 +246,7 @@ func main () { 除了文件句柄,我们还需要 `bufio` 的 `Writer`。我们以只写模式打开文件 `output.dat`,如果文件不存在则自动创建: ```go -outputFile, outputError := os.OpenFile(“output.dat”, os.O_WRONLY|os.O_ CREATE, 0666) +outputFile, outputError := os.OpenFile(“output.dat”, os.O_WRONLY|os.O_CREATE, 0666) ``` 可以看到,`OpenFile` 函数有三个参数:文件名、一个或多个标志(使用逻辑运算符“|”连接),使用的文件权限。 @@ -254,7 +254,7 @@ outputFile, outputError := os.OpenFile(“output.dat”, os.O_WRONLY|os.O_ CREAT 我们通常会用到以下标志: - `os.O_RDONLY`:只读 -- `os.WRONLY`:只写 +- `os.O_WRONLY`:只写 - `os.O_CREATE`:创建:如果指定文件不存在,就创建该文件。 - `os.O_TRUNC`:截断:如果指定文件已存在,就将该文件的长度截为0。 From 8b2144b86c3451f5c3ed49a131520bfef02fd47a Mon Sep 17 00:00:00 2001 From: Yixin Luo <18810541851@163.com> Date: Sun, 6 Mar 2016 16:08:02 +0800 Subject: [PATCH 17/23] =?UTF-8?q?tar=E5=91=BD=E4=BB=A4=E8=A7=A3=E5=8E=8B?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E9=9C=80=E8=A6=81-f=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E5=90=8D=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBook/02.3.md | 2 +- eBook/14.1.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eBook/02.3.md b/eBook/02.3.md index b283d84..6fcd02d 100644 --- a/eBook/02.3.md +++ b/eBook/02.3.md @@ -35,7 +35,7 @@ 从 [官方页面](https://golang.org/dl/) 或 [国内镜像](http://www.golangtc.com/download) 下载 Go 的源码包到你的计算机上,然后将解压后的目录 `go` 通过命令移动到 `$GOROOT` 所指向的位置。 wget https://storage.googleapis.com/golang/go.src.tar.gz - tar zxv go.src.tar.gz + tar -zxvf go.src.tar.gz sudo mv go $GOROOT 4. 构建 Go diff --git a/eBook/14.1.md b/eBook/14.1.md index 4e63bd5..b176618 100644 --- a/eBook/14.1.md +++ b/eBook/14.1.md @@ -143,7 +143,7 @@ At the end of main() // after 17 s 协程更有用的一个例子应该是在一个非常长的数组中查找一个元素。 -将数组分割为若干个不重复的切片,然后给每一个切片启动一个协程进行查找计算。这样许多并行的线程可以用来进行查找任务,整体的查找时间会缩短(除以协程的数量)。 +将数组分割为若干个不重复的切片,然后给每一个切片启动一个协程进行查找计算。这样许多并行的协程可以用来进行查找任务,整体的查找时间会缩短(除以协程的数量)。 ## 14.1.5 Go 协程(goroutines)和协程(coroutines) From e4a86cdaad11f481682571d98b6de2c2ee100315 Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Wed, 9 Mar 2016 17:45:42 +0800 Subject: [PATCH 18/23] fix typo --- eBook/04.4.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/eBook/04.4.md b/eBook/04.4.md index 3262bc2..f5256b1 100644 --- a/eBook/04.4.md +++ b/eBook/04.4.md @@ -8,7 +8,7 @@ 首先,它是为了避免像 C 语言中那样含糊不清的声明形式,例如:`int* a, b;`。在这个例子中,只有 a 是指针而 b 不是。如果你想要这两个变量都是指针,则需要将它们分开书写(你可以在 [Go 语言的声明语法](http://blog.golang.org/2010/07/gos-declaration-syntax.html) 页面找到有关于这个话题的更多讨论)。 -而在 Go 中,则可以和轻松地将它们都声明为指针类型: +而在 Go 中,则可以很轻松地将它们都声明为指针类型: ```go var a, b *int @@ -21,7 +21,7 @@ var a, b *int ```go var a int var b bool -var str string +var str string ``` 你也可以改写成这种形式: @@ -165,7 +165,7 @@ func main() { ![](../images/4.4.2_fig4.3.jpg?raw=true) -这个内存地址为称之为指针(你可以从上图中很清晰地看到,第 4.9 节将会详细说明),这个指针实际上也被存在另外的某一个字中。 +这个内存地址被称之为指针(你可以从上图中很清晰地看到,第 4.9 节将会详细说明),这个指针实际上也被存在另外的某一个字中。 同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。 @@ -192,7 +192,7 @@ func Printf(format string, list of variables to be printed) ```go fmt.Print("Hello:", 23) -``` +``` 将输出:`Hello: 23`。 @@ -255,7 +255,7 @@ a, b, c := 5, 7, "abc" (在 Go 语言中,这样省去了使用交换函数的必要) -空白标识符 `_` 也被用于抛弃值,如值 `5` 在:`_, b = 5, 7` 中被抛弃。 +空白标识符 `_` 也被用于抛弃值,如值 `5` 在:`_, b = 5, 7` 中被抛弃。 `_` 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。 From 5b2d1a73c7f98f9c2fa27da5d89e1295cab9b8b0 Mon Sep 17 00:00:00 2001 From: songleo Date: Mon, 14 Mar 2016 17:27:24 +0800 Subject: [PATCH 19/23] modified: eBook/10.1.md --- eBook/10.1.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eBook/10.1.md b/eBook/10.1.md index ee97b2d..179b586 100644 --- a/eBook/10.1.md +++ b/eBook/10.1.md @@ -35,7 +35,7 @@ t = new(T) 写这条语句的惯用方法是:`t := new(T)`,变量 `t` 是一个指向 `T`的指针,此时结构体字段的值是它们所属类型的零值。 -声明 `var t T` 也会给 `t` 分配内存,并零值化内存,但是这个时候 `t` 是类型T。在这两种方式中,`t` 通常被称做类型 T 的一个实例(instance)或对象(Object)。 +声明 `var t T` 也会给 `t` 分配内存,并零值化内存,但是这个时候 `t` 是类型T。在这两种方式中,`t` 通常被称做类型 T 的一个实例(instance)或对象(object)。 示例 10.1 [structs_fields.go](examples/chapter_10/structs_fields.go) 给出了一个非常简单的例子: @@ -85,7 +85,7 @@ v.i p.i ``` -初始化一个结构体实例(一个结构体字面量:struct-literal)的更简短和惯用的方式如下: +初始化一个结构体实例(一个结构体字面量:struct-literal)的更简短和惯用的方式如下: ```go ms := &struct1{10, 15.5, "Chris"} @@ -120,7 +120,7 @@ intr := Interval{end:5} (C) 在(A)中,值必须以字段在结构体定义时的顺序给出,**&** 不是必须的。(B)显示了另一种方式,字段名加一个冒号放在值的前面,这种情况下值的顺序不必一致,并且某些字段还可以被忽略掉,就像(C)中那样。 -结构体类型和字段的命名遵循可见性规则(第 [4.2](04.2.md) 节),一个导出的结构体类型中有些字段是导出的,另一些不是,这是可能的。 +结构体类型和字段的命名遵循可见性规则(第 [4.2](04.2.md) 节),一个导出的结构体类型中有些字段是导出的,另一些不是,这是可能的。 下图说明了结构体类型实例和一个指向它的指针的内存布局: @@ -138,7 +138,7 @@ type Point struct { x, y int } 类型 strcut1 在定义它的包 pack1 中必须是唯一的,它的完全类型名是:`pack1.struct1`。 -下面的例子 [Listing 10.2—person.go](examples/person.go) 显示了一个结构体 Person,一个方法,方法有一个类型为 `*Person` 的参数(因此对象本身是可以被改变的),以及三种调用这个方法的不同方式: +下面的例子 [Listing 10.2—person.go](examples/chapter_10/struct_pack/person.go) 显示了一个结构体 Person,一个方法,方法有一个类型为 `*Person` 的参数(因此对象本身是可以被改变的),以及三种调用这个方法的不同方式: ```go package main From 14049ae2819461ee1f4c51ac25c522c5b36d381a Mon Sep 17 00:00:00 2001 From: songleo Date: Mon, 14 Mar 2016 17:30:30 +0800 Subject: [PATCH 20/23] modified: eBook/10.1.md --- eBook/10.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/10.1.md b/eBook/10.1.md index 179b586..a97fb15 100644 --- a/eBook/10.1.md +++ b/eBook/10.1.md @@ -138,7 +138,7 @@ type Point struct { x, y int } 类型 strcut1 在定义它的包 pack1 中必须是唯一的,它的完全类型名是:`pack1.struct1`。 -下面的例子 [Listing 10.2—person.go](examples/chapter_10/struct_pack/person.go) 显示了一个结构体 Person,一个方法,方法有一个类型为 `*Person` 的参数(因此对象本身是可以被改变的),以及三种调用这个方法的不同方式: +下面的例子 [Listing 10.2—person.go](examples/chapter_10/person.go) 显示了一个结构体 Person,一个方法,方法有一个类型为 `*Person` 的参数(因此对象本身是可以被改变的),以及三种调用这个方法的不同方式: ```go package main From 6fc49e7787a5e1372bb3f45a41d462144cd81566 Mon Sep 17 00:00:00 2001 From: songleo Date: Mon, 14 Mar 2016 17:52:50 +0800 Subject: [PATCH 21/23] auto commit --- eBook/10.2.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/eBook/10.2.md b/eBook/10.2.md index 2bce19b..49b9f7e 100644 --- a/eBook/10.2.md +++ b/eBook/10.2.md @@ -2,7 +2,7 @@ ## 10.2.1 结构体工厂 -Go 语言不支持面向对象编程语言中那样的构造子方法,但是可以很容易的在 Go 中实现 “构造子工厂“ 方法。为了方便通常会为类型定义一个工厂,按惯例,工厂的名字以 new 或 New 开头。假设定义了如下的 File 结构体类型: +Go 语言不支持面向对象编程语言中那样的构造子方法,但是可以很容易的在 Go 中实现 “构造子工厂”方法。为了方便通常会为类型定义一个工厂,按惯例,工厂的名字以 new 或 New 开头。假设定义了如下的 File 结构体类型: ```go type File struct { @@ -29,7 +29,7 @@ func NewFile(fd int, name string) *File { f := NewFile(10, "./test.txt") ``` -在 Go 语言中常常像上面这样在工厂方法里使用初始化来简便的实现构造子。 +在 Go 语言中常常像上面这样在工厂方法里使用初始化来简便的实现构造函数。 如果 `File` 是一个结构体类型,那么表达式 `new(File)` 和 `&File{}` 是等价的。 @@ -41,7 +41,7 @@ f := NewFile(10, "./test.txt") **如何强制使用工厂方法** -通过应用可见性规则(参考第 4.2.1、9.5 节)就可以禁止使用 new 函数,强制用户使用工厂方法,从而使类型变成私有的,就像在面向对象语言中那样。 +通过应用可见性规则参考[4.2.1节](04.2.md)、[9.5 节](09.5.md)就可以禁止使用 new 函数,强制用户使用工厂方法,从而使类型变成私有的,就像在面向对象语言中那样。 ```go type matrix struct { @@ -70,9 +70,9 @@ new 和 make 这两个内置函数已经在第 [7.2.4](07.2.md) 节通过切片 现在为止我们已经见到了可以使用 `make()` 的三种类型中的其中两个: - slices / maps / channels(见第 14 章) + slices / maps / channels 参考[第14章](14.0.md) -下面的例子来说明了在映射上使用 new 和 make 的区别,以及可能的发生的错误: +下面的例子说明了在映射上使用 new 和 make 的区别以及可能发生的错误: 示例 10.4 new_make.go(不能编译) @@ -113,5 +113,5 @@ func main() { ## 链接 - [目录](directory.md) -- 上一节:[结构体定义)](10.1.md) +- 上一节:[结构体定义](10.1.md) - 下一节:[使用自定义包中的结构体](10.3.md) From f91237026ed18a7bbf3538de826cf0dacc29a9ee Mon Sep 17 00:00:00 2001 From: songleo Date: Mon, 14 Mar 2016 17:57:54 +0800 Subject: [PATCH 22/23] auto commit --- eBook/10.2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBook/10.2.md b/eBook/10.2.md index 49b9f7e..49e3b05 100644 --- a/eBook/10.2.md +++ b/eBook/10.2.md @@ -70,7 +70,7 @@ new 和 make 这两个内置函数已经在第 [7.2.4](07.2.md) 节通过切片 现在为止我们已经见到了可以使用 `make()` 的三种类型中的其中两个: - slices / maps / channels 参考[第14章](14.0.md) + slices / maps / channels(见第 14 章) 下面的例子说明了在映射上使用 new 和 make 的区别以及可能发生的错误: From de3bc9bfabeb9cc206f0715ddc4a4826a301bb6e Mon Sep 17 00:00:00 2001 From: songleo Date: Tue, 15 Mar 2016 12:34:44 +0800 Subject: [PATCH 23/23] auto commit --- eBook/10.3.md | 2 +- eBook/10.4.md | 4 ++-- eBook/10.5.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eBook/10.3.md b/eBook/10.3.md index b97cb0e..07a85df 100644 --- a/eBook/10.3.md +++ b/eBook/10.3.md @@ -2,7 +2,7 @@ 下面的例子中,main.go 使用了一个结构体,它来自 struct_pack 下的包 structPack。 -示例 10.5 structPack.go: +示例 10.5 [structPack.go](examples/chapter_10/struct_pack/structPack.go): ```go package structPack diff --git a/eBook/10.4.md b/eBook/10.4.md index e590e3e..b4d0732 100644 --- a/eBook/10.4.md +++ b/eBook/10.4.md @@ -1,8 +1,8 @@ # 10.4 带标签的结构体 -结构体中的字段除了有名字和类型外,还可以有一个可选的标签(tag):它是一个附属于字段的字符串,可以是文档或其他的重要标记。标签的内容不可以在一般的编程中使用,只有包 `reflect` 能获取它。我们将在下一章(第 11.10 节)中深入的探讨 `reflect`包,它可以在运行时自省类型、属性和方法,比如:在一个变量上调用 `reflect.TypeOf()` 可以获取变量的正确类型,如果变量是一个结构体类型,就可以通过 Field 来索引结构体的字段,然后就可以使用 Tag 属性。 +结构体中的字段除了有名字和类型外,还可以有一个可选的标签(tag):它是一个附属于字段的字符串,可以是文档或其他的重要标记。标签的内容不可以在一般的编程中使用,只有包 `reflect` 能获取它。我们将在下一章(第 [11.10 节](11.10.md))中深入的探讨 `reflect`包,它可以在运行时自省类型、属性和方法,比如:在一个变量上调用 `reflect.TypeOf()` 可以获取变量的正确类型,如果变量是一个结构体类型,就可以通过 Field 来索引结构体的字段,然后就可以使用 Tag 属性。 -示例 10.7 struct_tag.go: +示例 10.7 [struct_tag.go](examples/chapter_10/struct_tag.go): ```go package main diff --git a/eBook/10.5.md b/eBook/10.5.md index 70022ce..26ab16b 100644 --- a/eBook/10.5.md +++ b/eBook/10.5.md @@ -8,7 +8,7 @@ 考虑如下的程序: -示例 10.8 structs_anonymous_fields.go: +示例 10.8 [structs_anonymous_fields.go](examples/chapter_10/structs_anonymous_fields.go): ```go package main @@ -64,7 +64,7 @@ func main() { 另外一个例子: -示例 10.9 embedd_struct.go: +示例 10.9 [embedd_struct.go](examples/chapter_10/embedd_struct.go): ```go package main @@ -100,7 +100,7 @@ func main() { 当两个字段拥有相同的名字(可能是继承来的名字)时该怎么办呢? -1. 外层名字会覆盖内层名字(但是两者的内存空间都保留),这提供了一种重载字段或方法的方式 +1. 外层名字会覆盖内层名字(但是两者的内存空间都保留),这提供了一种重载字段或方法的方式; 2. 如果相同的名字在同一级别出现了两次,如果这个名字被程序使用了,将会引发一个错误(不使用没关系)。没有办法来解决这种问题引起的二义性,必须由程序员自己修正。 例子: