...

Frequently Asked Questions (FAQ)

Origins

由来

What is the purpose of the project?

该项目的目的在于什么?

No major systems language has emerged in over a decade, but over that time the computing landscape has changed tremendously. There are several trends:

十年以来,主流的系统级编程语言并未出现过,但在这期间,计算环境已经发生了巨大的变化。以下是一些趋势:

  • Computers are enormously quicker but software development is not faster.
  • Dependency management is a big part of software development today but the “header files” of languages in the C tradition are antithetical to clean dependency analysis—and fast compilation.
  • There is a growing rebellion against cumbersome type systems like those of Java and C++, pushing people towards dynamically typed languages such as Python and JavaScript.
  • Some fundamental concepts such as garbage collection and parallel computation are not well supported by popular systems languages.
  • The emergence of multicore computers has generated worry and confusion.

We believe it's worth trying again with a new language, a concurrent, garbage-collected language with fast compilation. Regarding the points above:

我们相信这值得重新尝试一种新的语言,一种并发的、带垃圾回收的、快速编译的语言。它需要满足以下几点:

  • It is possible to compile a large Go program in a few seconds on a single computer.
  • Go provides a model for software construction that makes dependency analysis easy and avoids much of the overhead of C-style include files and libraries.
  • Go's type system has no hierarchy, so no time is spent defining the relationships between types. Also, although Go has static types the language attempts to make types feel lighter weight than in typical OO languages.
  • Go is fully garbage-collected and provides fundamental support for concurrent execution and communication.
  • By its design, Go proposes an approach for the construction of system software on multicore machines.

A much more expansive answer to this question is available in the article, Go at Google: Language Design in the Service of Software Engineering.

关于此问题的更多答案见 Go在Google:软件工程服务中的语言设计 一文。

What is the status of the project?

该项目的状态如何?

Go became a public open source project on November 10, 2009. After a couple of years of very active design and development, stability was called for and Go 1 was released on March 28, 2012. Go 1, which includes a language specification, standard libraries, and custom tools, provides a stable foundation for creating reliable products, projects, and publications.

Go在2009年11月10日成为了公共开源项目。在两年的积极设计与开发之后,应稳定性要求, Go 1于2012年3月28日发布。 Go 1包含语言规范标准库定制工具, 它为创建可靠的产品、项目及出版物提供了稳定的基础。

With that stability established, we are using Go to develop programs, products, and tools rather than actively changing the language and libraries. In fact, the purpose of Go 1 is to provide long-term stability. Backwards-incompatible changes will not be made to any Go 1 point release. We want to use what we have to learn how a future version of Go might look, rather than to play with the language underfoot.

随着其稳定性的确立,我们使用Go来开发程序、产品以及工具,而非积极地更改语言与库。 实际上,Go 1的目的就是提供长期的稳定性。 不向前兼容的更改将不会对任何Go 1点发行版进行。我们想通过我们所拥有的来了解Go未来的版本将看起来如何, 而不是用语言阻碍前进的路。

Of course, development will continue on Go itself, but the focus will be on performance, reliability, portability and the addition of new functionality such as improved support for internationalization.

当然,Go本身的开发将继续进行,但重点将在性能、可靠性、可移植性和新功能的添加上,例如提升对国际化的支持。

There may well be a Go 2 one day, but not for a few years and it will be influenced by what we learn using Go 1 as it is today.

这在某一天可能会成为Go 2,但用不了几年,它就会被我们今天使用Go 1所学到的东西所影响。

What is the origin of the name?

名字的由来是什么?

“Ogle” would be a good name for a Go debugger.

对于Go的调试器,“Ogle”将会是个不错的名字。

What's the origin of the mascot?

吉祥物的由来是什么?

The mascot and logo were designed by Renée French, who also designed Glenda, the Plan 9 bunny. The gopher is derived from one she used for an WFMU T-shirt design some years ago. The logo and mascot are covered by the Creative Commons Attribution 3.0 license.

吉祥物与Logo由Renée French设计, 她也设计了Plan 9的小兔子Glenda。 Gopher衍生自她在几年前为WFMU设计的一件T恤衫。 其Logo与吉祥物以知识共享-署名3.0方式授权。

What is the history of the project?

该项目的历史是什么?

Robert Griesemer, Rob Pike and Ken Thompson started sketching the goals for a new language on the white board on September 21, 2007. Within a few days the goals had settled into a plan to do something and a fair idea of what it would be. Design continued part-time in parallel with unrelated work. By January 2008, Ken had started work on a compiler with which to explore ideas; it generated C code as its output. By mid-year the language had become a full-time project and had settled enough to attempt a production compiler. In May 2008, Ian Taylor independently started on a GCC front end for Go using the draft specification. Russ Cox joined in late 2008 and helped move the language and libraries from prototype to reality.

2007年9月21日,Robert Griesemer、Rob Pike与Ken Thompson在白板上开始了对新语言目标的描绘。 在几天之内,目标被制定成做事的计划,关于其未来的美妙想法便在此刻产生。 其设计在与工作无关的平行时间中继续。到了2008年1月,Ken开始了编译器的工作,并在其上探索各种想法; 它生成C代码并将其输出。到了年中,该语言成为了全职项目,拥有了充足的安排来尝试一个产品级编译器。 在2008年5月时,Ian Taylor根据规范草案独自开始了Go GCC前端的工作。2008年年末, Russ Cox的加入帮助将该语言与库从原型变成了现实。

Go became a public open source project on November 10, 2009. Many people from the community have contributed ideas, discussions, and code.

Go在2009年11月10日成为了公共开源项目。来自社区的许多人都可以贡献想法,参与讨论及编写代码。

Why are you creating a new language?

你们为什么要创造新的语言?

Go was born out of frustration with existing languages and environments for systems programming. Programming had become too difficult and the choice of languages was partly to blame. One had to choose either efficient compilation, efficient execution, or ease of programming; all three were not available in the same mainstream language. Programmers who could were choosing ease over safety and efficiency by moving to dynamically typed languages such as Python and JavaScript rather than C++ or, to a lesser extent, Java.

Go在既有语言与环境下进行系统编程的挫折中诞生。编程变得太难,对语言的选择有一定的责任。 我们必须在高效编译、高效执行或轻松编程之间选择其一,在同样主流的语言中,三者不能同时达到。 程序员们通过转移到Python和JavaScript之类的动态类型语言,而非C++或一定程度上的Java上, 来选择轻松在安全和效率之上。

Go is an attempt to combine the ease of programming of an interpreted, dynamically typed language with the efficiency and safety of a statically typed, compiled language. It also aims to be modern, with support for networked and multicore computing. Finally, it is intended to be fast: it should take at most a few seconds to build a large executable on a single computer. To meet these goals required addressing a number of linguistic issues: an expressive but lightweight type system; concurrency and garbage collection; rigid dependency specification; and so on. These cannot be addressed well by libraries or tools; a new language was called for.

Go试图成为结合解释型编程的轻松、动态类型语言的高效以及静态类型语言的安全的编译型语言。 它也打算成为现代的,支持网络与多核计算的语言。要满足这些目标,需要解决一些语言上的问题: 一个富有表达能力但轻量级的类型系统,并发与垃圾回收机制,严格的依赖规范等等。 这些无法通过库或工具解决好,必须创造新的语言。

The article Go at Google discusses the background and motivation behind the design of the Go language, as well as providing more detail about many of the answers presented in this FAQ.

文章 Go 在 Google 中讨论了Go语言设计的其背景和动机,关于本FAQ中的许多为题,该文章提供了更多详情。

What are Go's ancestors?

Go的前身是什么?

Go is mostly in the C family (basic syntax), with significant input from the Pascal/Modula/Oberon family (declarations, packages), plus some ideas from languages inspired by Tony Hoare's CSP, such as Newsqueak and Limbo (concurrency). However, it is a new language across the board. In every respect the language was designed by thinking about what programmers do and how to make programming, at least the kind of programming we do, more effective, which means more fun.

Go主要是C家族的(基本语法),从Pascal/Modula/Oberon家族引入了重要的东西(声明,包), 加上一些由Tony Hoare的CSP所激发的语言的理念,例如Newsqueak与Limbo(并发)。然而, 它是一个全新的语言。在各个方面上,该语言的设计都考虑到程序员做的事情以及如何去编程, 至少是我们进行的那种编程。更实际,也就意味着更有趣。

What are the guiding principles in the design?

其设计的指导原则是什么?

Programming today involves too much bookkeeping, repetition, and clerical work. As Dick Gabriel says, “Old programs read like quiet conversations between a well-spoken research worker and a well-studied mechanical colleague, not as a debate with a compiler. Who'd have guessed sophistication bought such noise?” The sophistication is worthwhile—no one wants to go back to the old languages—but can it be more quietly achieved?

如今的编程包含了太多记账式的、重复的、文书式的工作。就像Dick Gabriel说的那样: “老程序读起来就像健谈的研究工作者与善于学习的书呆子同事之间平和的对话, 而不像同编译器之间的争辩。谁会认为成熟必然带来杂乱?”这样的成熟是值得的—— 没有人想要回到老的语言——但它能更安静地被实现么?

Go attempts to reduce the amount of typing in both senses of the word. Throughout its design, we have tried to reduce clutter and complexity. There are no forward declarations and no header files; everything is declared exactly once. Initialization is expressive, automatic, and easy to use. Syntax is clean and light on keywords. Stuttering (foo.Foo* myFoo = new(foo.Foo)) is reduced by simple type derivation using the := declare-and-initialize construct. And perhaps most radically, there is no type hierarchy: types just are, they don't have to announce their relationships. These simplifications allow Go to be expressive yet comprehensible without sacrificing, well, sophistication.

Go试图在两种意义上减少文字的键入次数。贯穿其设计,我们试图减少混乱与复杂性。 它没有前置声明与头文件;任何东西都只声明一次。初始化富有表现力,自动且易于使用。 语法的关键字清晰而轻量。啰嗦的表达式(foo.Foo* myFoo = new(foo.Foo)) 可使用 := 声明并初始化结构,通过简单的类型推断来简化。 也许最根本的是,这里没有类型层级:类型就是类型,无需说明它们之间的关系。 这些简化允许Go无需牺牲成熟而富有表现力且易于理解。

Another important principle is to keep the concepts orthogonal. Methods can be implemented for any type; structures represent data while interfaces represent abstraction; and so on. Orthogonality makes it easier to understand what happens when things combine.

另一个重要的原则是保持概念正交。方法可被任何类型实现,结构代表数据而接口代表抽象等等。 正交性使一些东西相结合时发生的事情更易理解。

Usage

使用

Is Google using Go internally?

Google是否在内部使用Go?

Yes. There are now several Go programs deployed in production inside Google. A public example is the server behind golang.org. It's just the godoc document server running in a production configuration on Google App Engine.

是的。现在有几个Go程序正部署在Google的内部产品中。一个公共的例子就是在 http://golang.org 后台支持的服务。它仅仅是在 Google应用引擎 上配置的产品中运行的文档服务。

Other examples include the Vitess system for large-scale SQL installations and Google's download server, dl.google.com, which delivers Chrome binaries and other large installables such as apt-get packages.

其它例子包括用于大规模SQL安装的 Vitess 系统,以及Google的下载服务器 dl.google.com,它用于释放Chrome二进制文件 和其它类似 apt-get 的大型可安装包。

Go程序能否链接C/C++程序?

There are two Go compiler implementations, gc (the 6g program and friends) and gccgo. Gc uses a different calling convention and linker and can therefore only be linked with C programs using the same convention. There is such a C compiler but no C++ compiler. Gccgo is a GCC front-end that can, with care, be linked with GCC-compiled C or C++ programs.

现在有两种Go编译器实现,gc6g 程序及其同类)和 gccgoGc 使用了一种不同的调用约定和连接器, 因此只能与使用同样约定的C程序连接。现在只有这样的C编译器,而没有这样的C++编译器。 gccgo 为GCC的前端,可以小心地与GCC编译的C或C++程序连接。

The cgo program provides the mechanism for a “foreign function interface” to allow safe calling of C libraries from Go code. SWIG extends this capability to C++ libraries.

cgo 程序为“外部函数接口”提供了一种机制, 以允许从Go代码中安全地调用C库。SWIG为C++库扩展了这种能力。

Does Go support Google's protocol buffers?

Go是否支持Google的缓存协议?

A separate open source project provides the necessary compiler plugin and library. It is available at github.com/golang/protobuf/

一个单独的开源项目为此提供了必要的编译器插件与库。它可从 http://code.google.com/p/goprotobuf/ 获取。

Can I translate the Go home page into another language?

我能否将Go主页翻译为其它语言?

Absolutely. We encourage developers to make Go Language sites in their own languages. However, if you choose to add the Google logo or branding to your site (it does not appear on golang.org), you will need to abide by the guidelines at www.google.com/permissions/guidelines.html

完全可以。我们鼓励开发者将Go语言站点译成他们自己的语言。然而,如果你选择加入Google的Logo, 或品牌化推广你的站点(它不会出现在golang.org上), 你需要遵守 http://www.google.com/permissions/guidelines.html 上的指导方针。

Design

设计

What's up with Unicode identifiers?

Unicode标识符如何?

It was important to us to extend the space of identifiers from the confines of ASCII. Go's rule—identifier characters must be letters or digits as defined by Unicode—is simple to understand and to implement but has restrictions. Combining characters are excluded by design, for instance. Until there is an agreed external definition of what an identifier might be, plus a definition of canonicalization of identifiers that guarantees no ambiguity, it seemed better to keep combining characters out of the mix. Thus we have a simple rule that can be expanded later without breaking programs, one that avoids bugs that would surely arise from a rule that admits ambiguous identifiers.

从ASCII的限制中扩展标识符的空间对于我们是非常重要的。Go的规则—— 标识符字符必须是由Unicode定义的字母或数字——易于理解并实现,但也有限制。 比如结合式字符就排除在设计之外。在一个标识符是什么的外部定义可被接受, 且标识符的标准化定义可确保没有歧义之前,将结合式字符保持在混乱之外似乎更好些。 因此我们有一条简单的规则可以在不破坏程序的情况下以后扩展, 承认歧义性标识符的规则肯定会出现Bug,它可以避免此类Bug。

On a related note, since an exported identifier must begin with an upper-case letter, identifiers created from “letters” in some languages can, by definition, not be exported. For now the only solution is to use something like X日本語, which is clearly unsatisfactory; we are considering other options. The case-for-visibility rule is unlikely to change however; it's one of our favorite features of Go.

与此相关,由于可导出标识符必须以一个大写字母开始,根据定义, 以“字母”创建的标识符在一些语言中不能被导出。目前,唯一的解决方案就是使用一些如 X日本語 这样的形式,这明显无法令人满意;我们在考虑其它的选项。 大小写可视性规则无论如何都不会改变,因为这是我们最喜爱的Go特性之一。

Why does Go not have feature X?

为什么Go没有X特性?

Every language contains novel features and omits someone's favorite feature. Go was designed with an eye on felicity of programming, speed of compilation, orthogonality of concepts, and the need to support features such as concurrency and garbage collection. Your favorite feature may be missing because it doesn't fit, because it affects compilation speed or clarity of design, or because it would make the fundamental system model too difficult.

任何语言都会包含新奇的特性,也会省略掉一些人最喜爱的特性。Go的设计着眼于编程的快乐, 编译的素的,概念的正交以及一些必须支持的特性,例如并发机制和垃圾回收机制。 你最喜欢的特性可能由于不合适而缺失了,因为它影响了编译速度或设计的清晰度, 或因为它会使根本的系统模型变得太复杂。

If it bothers you that Go is missing feature X, please forgive us and investigate the features that Go does have. You might find that they compensate in interesting ways for the lack of X.

若Go因为缺失了特性 X 而烦扰到您了,请原谅我们。我们建议您研究一下Go所拥有的特性, 您可能会发现它们以有趣的方式弥补了 X 的缺失。

Why does Go not have generic types?

为什么Go没有泛型?

Generics may well be added at some point. We don't feel an urgency for them, although we understand some programmers do.

泛型可能会在某个时刻加入。我们对其并不感到紧急,尽管我们明白一些程序员会是这样。

Generics are convenient but they come at a cost in complexity in the type system and run-time. We haven't yet found a design that gives value proportionate to the complexity, although we continue to think about it. Meanwhile, Go's built-in maps and slices, plus the ability to use the empty interface to construct containers (with explicit unboxing) mean in many cases it is possible to write code that does what generics would enable, if less smoothly.

泛型是方便的,但它们也同时付出了类型系统与运行时的复杂性代价。 尽管我们还在继续思索着,但还未找到拥有与其复杂度相称价值的设计。 与此同时,Go内建的映射与切片,加上使用空接口来构造容器(带显式拆箱)的能力, 意味着如果顺利的话,在某些情况下,它可以写出泛型所能做到的代码。

This remains an open issue.

这保留为一个开放性问题。

Why does Go not have exceptions?

为什么Go没有异常处理?

We believe that coupling exceptions to a control structure, as in the try-catch-finally idiom, results in convoluted code. It also tends to encourage programmers to label too many ordinary errors, such as failing to open a file, as exceptional.

我们相信用 try-catch-finally 习语那样的控制结构连接成的异常, 其结果就是令人费解的代码。它往往也会怂恿程序员标注太多普通的错误, 诸如打开文件失败之类的作为异常。

Go takes a different approach. For plain error handling, Go's multi-value returns make it easy to report an error without overloading the return value. A canonical error type, coupled with Go's other features, makes error handling pleasant but quite different from that in other languages.

Go采用了一种不同的方法。对于朴素的错误处理,Go的多值返回使错误易于报告而无需重载返回值。 一个典型的错误类型,配合Go的其它特性, 使错误处理变得愉快而与众不同。

Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function's state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code.

Go也拥有内建函数的配合来标记出真正的异常状况并从中恢复。该恢复机制只会在函数的错误状态解除之后, 作为它的一部分执行,这足以处理灾难而无需格外的控制结构。如果使用得当,就能产生清晰的错误处理代码。

See the Defer, Panic, and Recover article for details.

详情见Defer、Panic、与Recover一文。

Why does Go not have assertions?

为什么Go没有断言?

Go doesn't provide assertions. They are undeniably convenient, but our experience has been that programmers use them as a crutch to avoid thinking about proper error handling and reporting. Proper error handling means that servers continue operation after non-fatal errors instead of crashing. Proper error reporting means that errors are direct and to the point, saving the programmer from interpreting a large crash trace. Precise errors are particularly important when the programmer seeing the errors is not familiar with the code.

Go不提供断言。它们无疑是很方便的,但我们的经验是,程序员们会使用它们作为依靠, 以避免考虑适当的错误处理和报告。适当的错误处理意味着服务器在非致命错误后可以继续运行, 而不会彻底崩溃。适当的错误报告意味着错误更加直接了当,最关键的一点是, 它能将程序员从解释大型崩溃的跟踪中拯救出来。精确的错误是极其重要的, 尤其在程序员们从不熟悉的代码中发现错误时。

We understand that this is a point of contention. There are many things in the Go language and libraries that differ from modern practices, simply because we feel it's sometimes worth trying a different approach.

我们明白这是一个争论的焦点。Go语言和库中的一些东西不同于现代的实践, 只不过是因为我们觉得偶尔尝试下不同的方法是值得的。

Why build concurrency on the ideas of CSP?

为什么以CSP思想来构建并发?

Concurrency and multi-threaded programming have a reputation for difficulty. We believe this is due partly to complex designs such as pthreads and partly to overemphasis on low-level details such as mutexes, condition variables, and memory barriers. Higher-level interfaces enable much simpler code, even if there are still mutexes and such under the covers.

并发和多线程编程以其困难著称。我们相信一部分原因是因为复杂的设计,例如pthreads; 一部分是因为过于强调低级的细节,例如互斥、条件变量以及内存屏障。更高级的接口可简化代码, 尽管像互斥这类的东西仍然存在。

One of the most successful models for providing high-level linguistic support for concurrency comes from Hoare's Communicating Sequential Processes, or CSP. Occam and Erlang are two well known languages that stem from CSP. Go's concurrency primitives derive from a different part of the family tree whose main contribution is the powerful notion of channels as first class objects. Experience with several earlier languages has shown that the CSP model fits well into a procedural language framework.

Hoare的通信序列过程(即CSP)为并发提供了高级的语言支持,它是最成功的模型之一。 Go的并发原语来自该家族树不同的部分,它最主要的贡献就是将强大的信道概念作为第一类对象。 从这些早期语言中得到的经验显现出CSP模型很适合用作过程式语言的框架。

Why goroutines instead of threads?

为什么使用Go程而非线程?

Goroutines are part of making concurrency easy to use. The idea, which has been around for a while, is to multiplex independently executing functions—coroutines—onto a set of threads. When a coroutine blocks, such as by calling a blocking system call, the run-time automatically moves other coroutines on the same operating system thread to a different, runnable thread so they won't be blocked. The programmer sees none of this, which is the point. The result, which we call goroutines, can be very cheap: they have little overhead beyond the memory for the stack, which is just a few kilobytes.

Go程是让使发易于使用的一部分。这个想法已经存在了一段时间,它是将独立执行的函数—— 协程——多路复用到一组线程上。当协程被阻塞,如通过调用一个阻塞的系统调用时, 运行时会在相同的操作系统线程上自动将其它的协程转移到一个不同的,可运行的, 不会被阻塞的线程上。重点是程序员不会看见。结果,我们称之为Go程,可以非常廉价: 除非它们在在长期运行的系统调用上花费了大量的时间,否则它们只会花费比栈多一点的内存, 那只有几KB而已。

To make the stacks small, Go's run-time uses resizable, bounded stacks. A newly minted goroutine is given a few kilobytes, which is almost always enough. When it isn't, the run-time grows (and shrinks) the memory for storing the stack automatically, allowing many goroutines to live in a modest amount of memory. The CPU overhead averages about three cheap instructions per function call. It is practical to create hundreds of thousands of goroutines in the same address space. If goroutines were just threads, system resources would run out at a much smaller number.

为了使栈很小,Go的运行时使用了分段式栈。一个新创建的Go程给定几KB,这几乎总是足够的。 当它不够时,运行时会自动地分配(并释放)扩展片段。每个函数调用平均需要大概三条廉价的指令。 这实际上是在相同的地址空间中创建了成百上千的Go程。如果Go程是线程的话,系统资源会更快地耗尽。

Why are map operations not defined to be atomic?

为什么映射操作不定义为原子性的?

After long discussion it was decided that the typical use of maps did not require safe access from multiple goroutines, and in those cases where it did, the map was probably part of some larger data structure or computation that was already synchronized. Therefore requiring that all map operations grab a mutex would slow down most programs and add safety to few. This was not an easy decision, however, since it means uncontrolled map access can crash the program.

经过长时间的讨论,决定了映射的典型使用无需从多Go程中安全地访问,在那些情况下, 映射可能是一些大型数据结构的一部分或已经同步的计算。 所以要求所有映射操作抓取互斥会减慢大部分程序并添加一些安全性。 这并不是个容易的决定,然而,这也就意味着不受控制的映射访问会使程序崩溃。

The language does not preclude atomic map updates. When required, such as when hosting an untrusted program, the implementation could interlock map access.

该语言并不排除原子性映射的更新,在需要时,例如在部署一个不信任的程序,该实现可以互锁映射访问。

Will you accept my language change?

你们会接受我对语言的修改么?

People often suggest improvements to the language—the mailing list contains a rich history of such discussions—but very few of these changes have been accepted.

人们经常会向该语言提出改进建议—— 邮件列表 中包含了此类讨论的丰富历史——但只有极少的修改会被接受。

Although Go is an open source project, the language and libraries are protected by a compatibility promise that prevents changes that break existing programs. If your proposal violates the Go 1 specification we cannot even entertain the idea, regardless of its merit. A future major release of Go may be incompatible with Go 1, but we're not ready to start talking about what that might be.

尽管Go是个开源项目,但该语言和库受兼容性保证 以确保更改不回破坏既有的程序。若你的建议违反了Go 1规范,不论它是否值得, 我们都不会接受。Go将来的主版本可能不会与Go 1兼容,但我们还不打算讨论这意味着什么。

Even if your proposal is compatible with the Go 1 spec, it might not be in the spirit of Go's design goals. The article Go at Google: Language Design in the Service of Software Engineering explains Go's origins and the motivation behind its design.

即使你的建议与Go 1规范兼容,它也可能不符合Go设计目标的精神。文章 Go在Google: 软件工程服务中的语言设计 解释了Go的起源机器设计背后的动机。

Types

类型

Is Go an object-oriented language?

Go是面向对象的语言吗?

Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).

既是也不是。尽管Go拥有类型和方法,也允许面向对象风格的编程,但它没有类型层级。 在Go中“接口”的概念提供了不同的方法,我们相信它易于使用且在某些方面更通用。 也有一些在其它类型中嵌入类型的方法,来提供类似(而非完全相同)的东西进行子类化。 此外,Go中的方法比C++或Java中的更通用:它们可被定义为任何种类的数据。 甚至是像普通的“未装箱”整数这样的内建类型。它们并不受结构(类)的限制。

Also, the lack of type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.

此外,类型层级的缺失也使Go中的“对象”感觉起来比C++或Java的更轻量级。

How do I get dynamic dispatch of methods?

我如何获得方法的动态分配?

The only way to have dynamically dispatched methods is through an interface. Methods on a struct or any other concrete type are always resolved statically.

拥有动态分配方法的唯一途径就是通过接口。结构或其它混合类型的方法总是静态地确定。

Why is there no type inheritance?

为什么没有类型继承?

Object-oriented programming, at least in the best-known languages, involves too much discussion of the relationships between types, relationships that often could be derived automatically. Go takes a different approach.

面向对象编程,至少在最著名的语言中,涉及了太多在类型之间关系的讨论, 关系总是可以自动地推断出来。Go则使用了一种不同的方法。

Rather than requiring the programmer to declare ahead of time that two types are related, in Go a type automatically satisfies any interface that specifies a subset of its methods. Besides reducing the bookkeeping, this approach has real advantages. Types can satisfy many interfaces at once, without the complexities of traditional multiple inheritance. Interfaces can be very lightweight—an interface with one or even zero methods can express a useful concept. Interfaces can be added after the fact if a new idea comes along or for testing—without annotating the original types. Because there are no explicit relationships between types and interfaces, there is no type hierarchy to manage or discuss.

不像需要程序员提前声明两个类型的关联,在Go中类型会自动满足任何接口, 以此实现其方法的子集。除了减少记账式编程外,这种方法拥有真正的优势。 类型可立刻满足一些接口,而没有传统多重继承的复杂性。 接口可以非常轻量——带一个甚至零个方法的接口能够表达一个有用的概念。 若出现了新的想法,或为了测试目的,接口其实可以在以后添加——而无需注释掉原来的类型。 由于在类型和接口之间没有明确的关系,也就无需管理或讨论类型层级。

It's possible to use these ideas to construct something analogous to type-safe Unix pipes. For instance, see how fmt.Fprintf enables formatted printing to any output, not just a file, or how the bufio package can be completely separate from file I/O, or how the image packages generate compressed image files. All these ideas stem from a single interface (io.Writer) representing a single method (Write). And that's only scratching the surface. Go's interfaces have a profound influence on how programs are structured.

用这些思想来构造一些类似于类型安全的Unix管道是可能的。例如,看看 fmt.Fprintf 如何能将格式化打印到任何输出而不只是文件, 或 bufio 包如何能从文件I/O中完全分离,或 image 包如何生成已压缩的图像文件。所有这些想法都来源于一个单一的接口 (io.Writer),都由一个单一的方法来表现(Write)。 而这只是表面文章。Go的接口在如何组织程序方面有着深刻的影响。

It takes some getting used to but this implicit style of type dependency is one of the most productive things about Go.

它需要一段时间来适应,但这种隐式的类型依赖是Go中最具生产力的东西之一。

Why is len a function and not a method?

为什么 len 是函数而非方法?

We debated this issue but decided implementing len and friends as functions was fine in practice and didn't complicate questions about the interface (in the Go type sense) of basic types.

我们讨论过这个问题,但显然在实践中将 len 及与其相关的功能实现为函数比较好, 而且这不会复杂化关于基本类型接口(在Go类型意义上)的问题。

Why does Go not support overloading of methods and operators?

为什么Go不支持方法和操作符的重载?

Method dispatch is simplified if it doesn't need to do type matching as well. Experience with other languages told us that having a variety of methods with the same name but different signatures was occasionally useful but that it could also be confusing and fragile in practice. Matching only by name and requiring consistency in the types was a major simplifying decision in Go's type system.

若方法分配无需很好地进行类型匹配,该方法即会被简化。其它语言的经验告诉我们, 拥有名字相同但签名不同的多种方法偶尔是有用的,但它也会在实践中造成混乱和不确定。 在Go的类型系统中,只通过名字进行匹配以及类型的一致性需求是主要的简化决策。

Regarding operator overloading, it seems more a convenience than an absolute requirement. Again, things are simpler without it.

至于操作符重载,它似乎并不能比绝对必要的东西提供更多便利。此外,没有它事情会变得更简单。

Why doesn't Go have "implements" declarations?

为什么Go没有 "implements" 声明?

A Go type satisfies an interface by implementing the methods of that interface, nothing more. This property allows interfaces to be defined and used without having to modify existing code. It enables a kind of structural typing that promotes separation of concerns and improves code re-use, and makes it easier to build on patterns that emerge as the code develops. The semantics of interfaces is one of the main reasons for Go's nimble, lightweight feel.

Go的类型通过实现接口的方法来满足该接口,仅此而已。这个性质允许定义接口并使用, 而无需修改已有的代码。它使用一种结构类型来帮助关系的分离并改进代码可重用性, 并使它更容易在为代码开发而出现的模式上构建。接口的语义学是Go的灵活、轻量感的主要原因之一。

See the question on type inheritance for more detail.

更多详情见类型继承的问题

How can I guarantee my type satisfies an interface?

我如何保证我的类型满足某个接口?

You can ask the compiler to check that the type T implements the interface I by attempting an assignment:

你可以通过尝试赋值来要求编译器检查类型 T 是否实现了接口 I

type T struct{}
var _ I = T{}   // Verify that T implements I.
type T struct{}
var _ I = T{}   // 确认T是否实现了I。

If T doesn't implement I, the mistake will be caught at compile time.

T 未实现 I,则错误会在编译时捕获。

If you wish the users of an interface to explicitly declare that they implement it, you can add a method with a descriptive name to the interface's method set. For example:

如果你希望接口的使用者显式地声明它们实现了它,你可以将一个带描述性名称的方法添加到该接口的方法集中:

type Fooer interface {
    Foo()
    ImplementsFooer()
}

A type must then implement the ImplementsFooer method to be a Fooer, clearly documenting the fact and announcing it in godoc's output.

然后类型必须实现 ImplementsFooer 方法成为 Fooergodoc的输出中清晰地记录了事实和通告。

type Bar struct{}
func (b Bar) ImplementsFooer() {}
func (b Bar) Foo() {}

Most code doesn't make use of such constraints, since they limit the utility of the interface idea. Sometimes, though, they're necessary to resolve ambiguities among similar interfaces.

大部分代码无需使用这类约束,因为它们限制了实用程序的接口思想。 但有时候,它们也需要解决相似接口之间的歧义。

Why doesn't type T satisfy the Equal interface?

为什么类型T不满足Equal接口?

Consider this simple interface to represent an object that can compare itself with another value:

考虑以下简单的接口,它表示一个可以将自身与另一个值进行比较的对象:

type Equaler interface {
    Equal(Equaler) bool
}

and this type, T:

以及此类型 T

type T int
func (t T) Equal(u T) bool { return t == u } // does not satisfy Equaler

Unlike the analogous situation in some polymorphic type systems, T does not implement Equaler. The argument type of T.Equal is T, not literally the required type Equaler.

不像在一些多态类型系统中类似的情况,T 并未实现 EqualerT.Equal 的实参类型为 T,而非字面上所需要的类型 Equalar

In Go, the type system does not promote the argument of Equal; that is the programmer's responsibility, as illustrated by the type T2, which does implement Equaler:

在Go中,类型系统并不提升 Equal 的实参,那是程序员的责任, 就以下类型 T2 所示,它实现了 Equaler

type T2 int
func (t T2) Equal(u Equaler) bool { return t == u.(T2) }  // satisfies Equaler
type T2 int
func (t T2) Equal(u Equaler) bool { return t == u.(T2) }  // 满足Equaler

Even this isn't like other type systems, though, because in Go any type that satisfies Equaler could be passed as the argument to T2.Equal, and at run time we must check that the argument is of type T2. Some languages arrange to make that guarantee at compile time.

即使它不像其它的类型系统也好,因为在Go中任何满足 Equaler 的类型都能作为实参传至 T2.Equal,并在运行时我们必须检查该实参是否为 T2类型。一些语言将其安排在编译时以保证做到这一点。

A related example goes the other way:

一个相关的例子是另一种情形:

type Opener interface {
   Open() Reader
}

func (t T3) Open() *os.File

In Go, T3 does not satisfy Opener, although it might in another language.

在Go中,T3 并不满足 Opener,尽管它在另一种语言中可能满足。

While it is true that Go's type system does less for the programmer in such cases, the lack of subtyping makes the rules about interface satisfaction very easy to state: are the function's names and signatures exactly those of the interface? Go's rule is also easy to implement efficiently. We feel these benefits offset the lack of automatic type promotion. Should Go one day adopt some form of generic typing, we expect there would be a way to express the idea of these examples and also have them be statically checked.

在相同情况下,Go的类型系统确实为程序员做的更少, 子类型化的缺乏使关于接口满足的规则非常容易制订: 函数的名字和签名完全就是那些接口吗?Go的规则也容易高效地实现。 我们感觉这些效益抵消了自动类型提升的缺失。Go在某天应当采取一些泛型的形式, 我们期望会有一些方式来表达这些例子的想法,且也拥有静态检查。

Can I convert a []T to an []interface{}?

我能否将[]T转换为[]interface{}?

Not directly, because they do not have the same representation in memory. It is necessary to copy the elements individually to the destination slice. This example converts a slice of int to a slice of interface{}:

不能直接转换,因为它们在内存中的表示并不相同。必须单独地将元素复制到目标切片。 下面的例子将 int 切片转换为 interface{} 切片:

t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
    s[i] = v
}

Why is my nil error value not equal to nil?

为什么我的nil错误值不等于nil?

Under the covers, interfaces are implemented as two elements, a type and a value. The value, called the interface's dynamic value, is an arbitrary concrete value and the type is that of the value. For the int value 3, an interface value contains, schematically, (int, 3).

在底层,接口作为两个元素实现:一个类型和一个值。该值被称为接口的动态值, 它是一个任意的具体值,而该接口的类型则为该值的类型。对于 int 值3, 一个接口值示意性地包含(int, 3)。

An interface value is nil only if the inner value and type are both unset, (nil, nil). In particular, a nil interface will always hold a nil type. If we store a pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (*int, nil). Such an interface value will therefore be non-nil even when the pointer inside is nil.

只有在内部值和类型都未设置时(nil, nil),一个接口的值才为 nil。特别是,一个 nil 接口将总是拥有一个 nil 类型。若我们在一个接口值中存储一个 *int 类型的指针,则内部类型将为 *int,无论该指针的值是什么:(*int, nil)。 因此,这样的接口值会是非 nil 的,即使在该指针的内部为 nil

This situation can be confusing, and often arises when a nil value is stored inside an interface value such as an error return:

这种情况会让人迷惑,而且当 nil 值存储在接口值内部时这种情况总是发生, 例如错误返回:

func returnsError() error {
	var p *MyError = nil
	if bad() {
		p = ErrBad
	}
	return p // Will always return a non-nil error.
}
func returnsError() error {
	var p *MyError = nil
	if bad() {
		p = ErrBad
	}
	return p // 将总是返回一个非nil错误。
}

If all goes well, the function returns a nil p, so the return value is an error interface value holding (*MyError, nil). This means that if the caller compares the returned error to nil, it will always look as if there was an error even if nothing bad happened. To return a proper nil error to the caller, the function must return an explicit nil:

如果一切顺利,该函数会返回一个 nilp, 因此该返回值为拥有(*MyError, nil)的 error 接口值。这也就意味着如果调用者将返回的错误与 nil 相比较, 它将总是看上去有错误,即便没有什么坏事发生。要向调用者返回一个适当的 nil error,该函数必须返回一个显式的 nil

func returnsError() error {
	if bad() {
		return ErrBad
	}
	return nil
}

It's a good idea for functions that return errors always to use the error type in their signature (as we did above) rather than a concrete type such as *MyError, to help guarantee the error is created correctly. As an example, os.Open returns an error even though, if not nil, it's always of concrete type *os.PathError.

这对于总是在签名中使用 error 类型返回错误(正如我们上面做的)而非像 *MyError 这样具体类型的函数来说是个不错的主意,它可以帮助确保错误被正确地创建。 例如,即使 os.Open 返回一个 error, 若非 nil 的话,它总是具体的类型 *os.PathError

Similar situations to those described here can arise whenever interfaces are used. Just keep in mind that if any concrete value has been stored in the interface, the interface will not be nil. For more information, see The Laws of Reflection.

对于那些描述,无论接口是否被使用,相似的情形都会出现。只要记住,如果任何具体的值已被存储在接口中, 该接口就不为 nil。更多信息请访问反射法则

Why are there no untagged unions, as in C?

为什么没有像C那样的无标签联合?

Untagged unions would violate Go's memory safety guarantees.

无标签联合会违反Go的内存安全保证。

Why does Go not have variant types?

为什么没有变体类型?

Variant types, also known as algebraic types, provide a way to specify that a value might take one of a set of other types, but only those types. A common example in systems programming would specify that an error is, say, a network error, a security error or an application error and allow the caller to discriminate the source of the problem by examining the type of the error. Another example is a syntax tree in which each node can be a different type: declaration, statement, assignment and so on.

变体类型,亦称为代数类型,它提供了一种方法来指定一个值可以获得其它类型集中的一个类型, 但仅限于那些类型。在系统编程中一个常见的例子是指定了一个错误,具体来说,一个网络错误、 一个安全性错误或一个应用错误,并允许调用者通过检查该错误的类型来辨别错误的根源。 另一个例子是在语法树中的每个节点可以为不同的类型:声明、语句、赋值等等。

We considered adding variant types to Go, but after discussion decided to leave them out because they overlap in confusing ways with interfaces. What would happen if the elements of a variant type were themselves interfaces?

我们考虑过将变体类型添加到Go中,但经过讨论后决定远离它们,因为它们以混乱的方式与接口重叠。 如果变体类型的元素是它们自己的接口会发生什么?

Also, some of what variant types address is already covered by the language. The error example is easy to express using an interface value to hold the error and a type switch to discriminate cases. The syntax tree example is also doable, although not as elegantly.

此外,变体类型从事的一些工作已被该语言所覆盖。上面有关错误的例子很容易使用接口值来表达, 以此控制错误及类型转换来辨别状况。关于语法树的例子也可以这么做,尽管不太优雅。

Values

Why does Go not provide implicit numeric conversions?

为什么Go不提供隐式数值转换?

The convenience of automatic conversion between numeric types in C is outweighed by the confusion it causes. When is an expression unsigned? How big is the value? Does it overflow? Is the result portable, independent of the machine on which it executes? It also complicates the compiler; “the usual arithmetic conversions” are not easy to implement and inconsistent across architectures. For reasons of portability, we decided to make things clear and straightforward at the cost of some explicit conversions in the code. The definition of constants in Go—arbitrary precision values free of signedness and size annotations—ameliorates matters considerably, though.

在C中数值类型之间的自动转换所造成的混乱超过了它的便利。一个表达式什么时候是无符号的? 这个值有多大?它可以被覆盖吗?该结果可被移植,独立于它所执行的机器吗? 这也使编译器陷入了麻烦;“一般的算数转换”不容易实现且在跨架构时不一致。 出于可移植性的原因,我们决定在代码中付出一些显式转换的代价,来使事情变得清晰而直接。 而在Go中对常量的定义——无符号和大小注解的任意精度的值——对事情有却重大的改善。

A related detail is that, unlike in C, int and int64 are distinct types even if int is a 64-bit type. The int type is generic; if you care about how many bits an integer holds, Go encourages you to be explicit.

一个相关的细节是,不像在C中,intint64 是不同的类型, 即使 int 是一个64位的类型。int 类型是一般的, 如果你关心一个整数占多少位,Go会鼓励你搞清楚它。

A blog post, title Constants, explores this topic in more detail.

常量一文探讨了此话题的更多细节。

Why are maps built in?

为什么映射是内建的?

The same reason strings are: they are such a powerful and important data structure that providing one excellent implementation with syntactic support makes programming more pleasant. We believe that Go's implementation of maps is strong enough that it will serve for the vast majority of uses. If a specific application can benefit from a custom implementation, it's possible to write one but it will not be as convenient syntactically; this seems a reasonable tradeoff.

同样的理由是:它们是如此地强大和重要的数据结构,提供了一种有句法上支持的卓越的实现, 使编程更加惬意。我们相信Go的映射实现足够健壮以服务于绝大多数的使用。 若一个具体的应用可从定制的实现中收益,那就可以写一个,但它将在语法上不那么方便, 这似乎是个合理的权衡。

Why don't maps allow slices as keys?

为什么映射不允许将切片作为键?

Map lookup requires an equality operator, which slices do not implement. They don't implement equality because equality is not well defined on such types; there are multiple considerations involving shallow vs. deep comparison, pointer vs. value comparison, how to deal with recursive types, and so on. We may revisit this issue—and implementing equality for slices will not invalidate any existing programs—but without a clear idea of what equality of slices should mean, it was simpler to leave it out for now.

映射查找需要一个相等性操作符,而切片并未实现它。它们不能实现相等性, 因为相等性没有在这种类型上很好地定义,这里有多个因素,涉及到浅层次与深层次之间的比较, 指针与值之间的比较,如何处理递归类型等等。我们可能会重新审视这个问题——并为切片实现相等性, 而不会使任何已存在的程序失效——但切片的相等性意味着什么还没有一个清晰的概念, 现在最简单的方法就是远离它。

In Go 1, unlike prior releases, equality is defined for structs and arrays, so such types can be used as map keys. Slices still do not have a definition of equality, though.

不像之前的发布版,在Go 1中为结构和数组的相等性下了定义,因此这样的类型可被用作映射的键。 然而,切片仍然没有相等性的定义。

Why are maps, slices, and channels references while arrays are values?

为什么映射、切片和信道是引用,而数组是值?

There's a lot of history on that topic. Early on, maps and channels were syntactically pointers and it was impossible to declare or use a non-pointer instance. Also, we struggled with how arrays should work. Eventually we decided that the strict separation of pointers and values made the language harder to use. Changing these types to act as references to the associated, shared data structures resolved these issues. This change added some regrettable complexity to the language but had a large effect on usability: Go became a more productive, comfortable language when it was introduced.

这个话题有很长的历史。在早期,映射和在语法上是指针,它不可能通过声明或使用非指针来实例化。 此外,我们也曾在数组该如何工作的问题上挣扎。最后我们认为,指针和值的严格分离会使语言更难用。 将这些类型的行为修改成对关联的引用,共享数据结构解决了这些问题。 这种修改向该语言加入了一些令人遗憾的复杂性,但它们在可用性上有更大的效果: 当它被引入时,Go成为了一门更高效,更舒适的语言。

Writing Code

编写代码

How are libraries documented?

如何将库文档化?

There is a program, godoc, written in Go, that extracts package documentation from the source code. It can be used on the command line or on the web. An instance is running at golang.org/pkg/. In fact, godoc implements the full site at golang.org/.

有一个用Go编写的程序 godoc 它可以从源码中提取包文档。 它可以在命令行或Web中使用。一个例子就是它运行在http://golang.org/pkg/上。 实际上,godoc 已经在http://golang.org/上实现了完整的网站。

Is there a Go programming style guide?

有没有Go编程风格指南?

Eventually, there may be a small number of rules to guide things like naming, layout, and file organization. The document Effective Go contains some style advice. More directly, the program gofmt is a pretty-printer whose purpose is to enforce layout rules; it replaces the usual compendium of do's and don'ts that allows interpretation. All the Go code in the repository has been run through gofmt.

最后,可能有少量的规则来指导像命名、布局以及文件组织这类的事情。 文档高效Go编程包含了一些风格建议。 更直接的说,程序 gofmt 是一个美观打印工具,它的目的在于强制实施布局规则, 它可以取代要解释该做什么和不该做什么的纲要所有代码仓库中的Go代码都运行过了 gofmt

The document titled Go Code Review Comments is a collection of very short essays about details of Go idiom that are often missed by programmers. It is a handy reference for people doing code reviews for Go projects.

文档 Go 代码审核评注 收集了 Go 的习惯用法,程序员们经常忽视它们。它是一份便于审核人员审核Go项目代码的引用资料。

How do I submit patches to the Go libraries?

我如何向Go库提交补丁?

The library sources are in the src directory of the repository. If you want to make a significant change, please discuss on the mailing list before embarking.

库的源码在 go/src/pkg 中。如果你想进行重大的更改,请在动手前在邮件列表中讨论。

See the document Contributing to the Go project for more information about how to proceed.

关于如何进行的更多信息请访问为Go项目做贡献

Why does "go get" use HTTPS when cloning a repository?

为什么“go get”在克隆代码仓库时使用HTTPS?

Companies often permit outgoing traffic only on the standard TCP ports 80 (HTTP) and 443 (HTTPS), blocking outgoing traffic on other ports, including TCP port 9418 (git) and TCP port 22 (SSH). When using HTTPS instead of HTTP, git enforces certificate validation by default, providing protection against man-in-the-middle, eavesdropping and tampering attacks. The go get command therefore uses HTTPS for safety.

公司通常只允许标准TCP端口80(HTTP)和443(HTTPS)的向外通行,而封闭其它端口的向外通行, 包括TCP端口9418(git)和TCP端口22(SSH)。当使用HTTPS代替HTTP时,git 默认会强制执行证书认证来提供保护,防止中间人的窃听和篡改攻击。因此为了安全, go get 命令就使用了HTTPS。

If you use git and prefer to push changes through SSH using your existing key it's easy to work around this. For GitHub, try one of these solutions:

如果你更喜欢 git 用你现有的密钥通过SSH来推送更改,那也很容易。 对于GitHub,试试下面的解决方案:

  • Manually clone the repository in the expected package directory:
    $ cd $GOPATH/src/github.com/username
    $ git clone git@github.com:username/package.git
    
  • Force git push to use the SSH protocol by appending these two lines to ~/.gitconfig:
    [url "git@github.com:"]
    	pushInsteadOf = https://github.com/
    

How should I manage package versions using "go get"?

我如何通过“go get”来管理包的版本?

"Go get" does not have any explicit concept of package versions. Versioning is a source of significant complexity, especially in large code bases, and we are unaware of any approach that works well at scale in a large enough variety of situations to be appropriate to force on all Go users. What "go get" and the larger Go toolchain do provide is isolation of packages with different import paths. For example, the standard library's html/template and text/template coexist even though both are "package template". This observation leads to some advice for package authors and package users.

“go get”并没有明确的包版本概念。版本是最主要的复杂性来源,尤其是在大型代码的基础上, 我们并没有适合所有Go用户的万能解决方案。“go get”和更大的Go工具链,仅能为包提供不同的导入路径来隔离它们。 例如,尽管标准库的 html/templatetext/template 都是“template包”,但它们能够共存。下面为包的作者和用户给出一些意见和建议。

Packages intended for public use should try to maintain backwards compatibility as they evolve. The Go 1 compatibility guidelines are a good reference here: don't remove exported names, encourage tagged composite literals, and so on. If different functionality is required, add a new name instead of changing an old one. If a complete break is required, create a new package with a new import path.

公用的包应为它们的演化尽量保持向下兼容性。Go 1 兼容性方针 是个不错的参考:不要移除可导出的名称,鼓励标记出复合字面,等等。若需要不同的功能,请添加一个新的名字, 而非更改旧的名字。若需要完全打破兼容性,请用新的导入路径创建一个新的包。

If you're using an externally supplied package and worry that it might change in unexpected ways, the simplest solution is to copy it to your local repository. (This is the approach Google takes internally.) Store the copy under a new import path that identifies it as a local copy. For example, you might copy "original.com/pkg" to "you.com/external/original.com/pkg". gomvpkg is one tool to help automate this process.

若您使用的是外部提供的包,并担心它会以意想不到的方式改变,最简单的解决方案就是把它复制到你的本地仓库中。 (这是Google内部采用的方法。)将该副本存储在一个新的导入路径中,以此来标识出它是个本地的副本。 例如,你可以将“original.com/pkg”复制成“you.com/external/original.com/pkg”。 Keith Rarick的goven就是个帮你自动处理它的工具。

Pointers and Allocation

指针与分配

When are function parameters passed by value?

函数形参在什么时候传值?

As in all languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the pointer, but not the data it points to. (See the next section for a discussion of how this affects method receivers.)

和所有C家族中的语言一样,Go中的所有懂喜都通过值来传递。也就是说, 函数总是会获得向它传递的东西的一份副本,就好像有一个赋值语句向它的形参赋值。 例如,将一个 int 值传入一个函数就会创建该 int 值的一份副本,而传入一个指针值则会创建该指针的一份副本,而不是它所指向的数据。 (关于它如何影响方法接收器的讨论见下一节。)

Map and slice values behave like pointers: they are descriptors that contain pointers to the underlying map or slice data. Copying a map or slice value doesn't copy the data it points to. Copying an interface value makes a copy of the thing stored in the interface value. If the interface value holds a struct, copying the interface value makes a copy of the struct. If the interface value holds a pointer, copying the interface value makes a copy of the pointer, but again not the data it points to.

映射和切片值的行为就像指针一样:它们就是包含指向基本映射或切片数据的指针的描述符。 复制一个映射或切片会创建一个存储在接口值中的东西的一个副本。若该接口值保存了一个结构, 复制该接口值则会创建一个该结构的副本。若该接口值保存了一个指针, 复制该接口值则会创建一个该指针的副本,而且同样不是它所指向的数据。

When should I use a pointer to an interface?

我应在何时使用接口指针?

Almost never. Pointers to interface values arise only in rare, tricky situations involving disguising an interface value's type for delayed evaluation.

几乎用不着。接口值指针极其罕见,复杂情况涉及掩饰接口值的类型以延迟求值。

It is however a common mistake to pass a pointer to an interface value to a function expecting an interface. The compiler will complain about this error but the situation can still be confusing, because sometimes a pointer is necessary to satisfy an interface. The insight is that although a pointer to a concrete type can satisfy an interface, with one exception a pointer to an interface can never satisfy an interface.

不过有一个常见的错误,就是将接口值指针传递给一个期望接受接口的函数。编译器会抱怨这种错误, 但这种情况仍然会产生混淆,因为有时为了满足一个接口,指针是必要的。 这种洞察力就在于,尽管常量类型的指针可以满足一个接口,但还有一个例外:接口指针永远无法满足接口

Consider the variable declaration,

考虑此变量声明:

var w io.Writer

The printing function fmt.Fprintf takes as its first argument a value that satisfies io.Writer—something that implements the canonical Write method. Thus we can write

打印函数 fmt.Fprintf 将其第一个实参作为接口值,该值满足像 io.Writer 这类实现了标准 Write 方法的接口。 因此我们可以这样写:

fmt.Fprintf(w, "hello, world\n")

If however we pass the address of w, the program will not compile.

If however we pass the address of w, the program will not compile. 但如果我们传递了 w 的地址,该程序将不会编译。

fmt.Fprintf(&w, "hello, world\n") // Compile-time error.
fmt.Fprintf(&w, "hello, world\n") // 编译时错误。

The one exception is that any value, even a pointer to an interface, can be assigned to a variable of empty interface type (interface{}). Even so, it's almost certainly a mistake if the value is a pointer to an interface; the result can be confusing.

一个例外就是任何值,甚至接口指针都能被赋予一个空接口类型(interface{})的变量。 即便如此,若该值是一个指针接口,就几乎可以断定它是个错误;其结果仍会产生混淆。

Should I define methods on values or pointers?

我应当为值或指针定义方法吗?

func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct)  valueMethod()   { } // method on value
func (s *MyStruct) pointerMethod() { } // 为指针定义的方法
func (s MyStruct)  valueMethod()   { } // 为值定义的方法

For programmers unaccustomed to pointers, the distinction between these two examples can be confusing, but the situation is actually very simple. When defining a method on a type, the receiver (s in the above examples) behaves exactly as if it were an argument to the method. Whether to define the receiver as a value or as a pointer is the same question, then, as whether a function argument should be a value or a pointer. There are several considerations.

对于不习惯指针的程序员,这两个例子之间的差别会造成混乱,但这种情况其实是非常简单的。 当为一个类型定义了一个方法,则接收者(上面例子中的 s)的表现正好就是该方法的实参。 将接收者定义为值还是指针都是一样的问题,就像一个函数的实参应该是值还是指针一样。 有几点需要考虑的地方。

First, and most important, does the method need to modify the receiver? If it does, the receiver must be a pointer. (Slices and maps act as references, so their story is a little more subtle, but for instance to change the length of a slice in a method the receiver must still be a pointer.) In the examples above, if pointerMethod modifies the fields of s, the caller will see those changes, but valueMethod is called with a copy of the caller's argument (that's the definition of passing a value), so changes it makes will be invisible to the caller.

首先,也是最重要的一点,方法需要修改接收者吗?如果是,则接收者必须是一个指针。 (切片和映射的行为类似于引用,所以它们的状况有一点微妙,但比如说要改变方法中切片的长度, 则接受者仍然必须是指针。)在上面的例子中,如果 pointerMethod 修改了 s 的字段,那么调用者将观察到那些改变,但 valueMethod 是由调用者实参的副本调用的(这是传值的规定),因此对它的更改对于调用者来说是不可见的。

By the way, pointer receivers are identical to the situation in Java, although in Java the pointers are hidden under the covers; it's Go's value receivers that are unusual.

顺便一提,指针接收者在Java中的情况和Go是相同的,尽管在Java中指针隐藏在幕后; 而Go的值接受者则不相同。

Second is the consideration of efficiency. If the receiver is large, a big struct for instance, it will be much cheaper to use a pointer receiver.

其次是效率问题的考虑。若接受者很大,比如说一个大型的 struct, 使用指针接收器将更廉价。

Next is consistency. If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used. See the section on method sets for details.

接着是一致性问题。若某些类型的方法必须拥有指针接收者,则其余的也应该这样, 因此不管该类型被如何使用,方法集都始终如一。更多详情见方法集一节。

For types such as basic types, slices, and small structs, a value receiver is very cheap so unless the semantics of the method requires a pointer, a value receiver is efficient and clear.

对于诸如基本类型、切片以及小型 struct 这样的类型,值接收者是非常廉价的, 因此除非该方法的语义需要一个指针,一个有效而清楚的值接收者。

What's the difference between new and make?

new与make之间有什么不同?

In short: new allocates memory, make initializes the slice, map, and channel types.

简单来说:new 分配内存,make 初始化切片、映射和信道类型。

See the relevant section of Effective Go for more details.

更多信息请访问高效Go编程的相关章节

What is the size of an int on a 64 bit machine?

int 在64位机器上的大小是多少?

The sizes of int and uint are implementation-specific but the same as each other on a given platform. For portability, code that relies on a particular size of value should use an explicitly sized type, like int64. Prior to Go 1.1, the 64-bit Go compilers (both gc and gccgo) used a 32-bit representation for int. As of Go 1.1 they use a 64-bit representation. On the other hand, floating-point scalars and complex numbers are always sized: float32, complex64, etc., because programmers should be aware of precision when using floating-point numbers. The default size of a floating-point constant is float64.

intuint 的大小取决于具体实现, 但在给定平台上它们彼此之间相同。64位Go编译器(gc和gccgo)使用32位来表示 int。依赖于值具体大小的代码应当使用确定大小的类型,比如说 int64。另一方面,浮点数标量和复数的大小总是固定的:float32complex64 等等,因为程序员在使用浮点数时应当知道精度。 浮点数常量的默认大小为 float64

At the moment, all implementations use 32-bit ints, an essentially arbitrary decision. However, we expect that int will be increased to 64 bits on 64-bit architectures in a future release of Go.

目前,所有的实现都用32位int,基本上是很武断的决定。然而,我们希望在未来的Go发行版中, int 会在64位架构上增加到64位。

How do I know whether a variable is allocated on the heap or the stack?

我如何知道变量分配在堆上还是栈上?

From a correctness standpoint, you don't need to know. Each variable in Go exists as long as there are references to it. The storage location chosen by the implementation is irrelevant to the semantics of the language.

从正确性立场上看,你无须了解它。Go中存在的每一个变量都只需引用它就行。 由实现选择的存储位置与该语言的语义无关。

The storage location does have an effect on writing efficient programs. When possible, the Go compilers will allocate variables that are local to a function in that function's stack frame. However, if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable on the garbage-collected heap to avoid dangling pointer errors. Also, if a local variable is very large, it might make more sense to store it on the heap rather than the stack.

存储位置对于编写有效的程序来说并无影响。如果可能,Go编译器会在该函数的栈帧中, 分配该函数的局部变量。然而,如果编译器不能证明在该函数返回后,该变量不再被引用, 那么编译器必须在垃圾回收的堆上分配该变量,以避免悬空指针错误。此外,若局部变量非常大, 它可能会更合理地将变量存储在堆而非栈中。

In the current compilers, if a variable has its address taken, that variable is a candidate for allocation on the heap. However, a basic escape analysis recognizes some cases when such variables will not live past the return from the function and can reside on the stack.

在当前编译器中,若一个变量的地址已被占用,该变量对于堆上的分配来说就是个候选的。 然而,当这样的变量不会在该函数返回后继续存在并驻留在栈上时, 一个基本的逃逸分析就能识别这一情况。

Why does my Go process use so much virtual memory?

为什么我的Go进程会使用那么多虚拟内存?

The Go memory allocator reserves a large region of virtual memory as an arena for allocations. This virtual memory is local to the specific Go process; the reservation does not deprive other processes of memory.

Go的内存分配器在虚拟内存中预留了一大块区域作为分配的地方。这块虚拟内存局部于具体的Go进程, 而这种预留并不会剥夺内存中的其它进程。

To find the amount of actual memory allocated to a Go process, use the Unix top command and consult the RES (Linux) or RSIZE (Mac OS X) columns.

要得出分配给某个Go进程的实际内存数额,请使用Unix的 top 命令并查阅 RES(Linux)或 RSIZE(Mac OS X)一列。

Concurrency

并发

What operations are atomic? What about mutexes?

什么操作是原子性的?什么是互斥性的?

We haven't fully defined it all yet, but some details about atomicity are available in the Go Memory Model specification.

我们现在还没有完整地定义它,不过一些关于原子性的细节还是可以从 Go内存模型规范中找到的。

Regarding mutexes, the sync package implements them, but we hope Go programming style will encourage people to try higher-level techniques. In particular, consider structuring your program so that only one goroutine at a time is ever responsible for a particular piece of data.

至于互斥性,sync包实现了它们, 但我们希望Go的编程风格会鼓励人们尝试更高级的技巧。特别是,考虑结构化你的程序, 以便一次只用一个Go程来负责一块特定的数据。

Do not communicate by sharing memory. Instead, share memory by communicating.

不要通过共享内存来进行通信,而应该通过通信来共享内存。

See the Share Memory By Communicating code walk and its associated article for a detailed discussion of this concept.

关于这个概念的详细讨论,请参阅 通过通信共享内存 的代码漫步及其 相关文章

Why doesn't my multi-goroutine program use multiple CPUs?

为什么我的多Go程程序不能使用多个CPU?

You must set the GOMAXPROCS shell environment variable or use the similarly-named function of the runtime package to allow the run-time support to utilize more than one OS thread.

你必须设置GOMAXPROCS外壳环境变量或使用运行时包中同名的函数 function 以允许运行时支持利用不止一个的操作系统线程。

Programs that perform parallel computation should benefit from an increase in GOMAXPROCS. However, be aware that concurrency is not parallelism.

执行并行计算的程序应该能从增加 GOMAXPROCS 中获益。不过,你必须意识到 并发不是并行

Why does using GOMAXPROCS > 1 sometimes make my program slower?

为什么使用 GOMAXPROCS > 1 有时会使我的程序变慢?

It depends on the nature of your program. Problems that are intrinsically sequential cannot be sped up by adding more goroutines. Concurrency only becomes parallelism when the problem is intrinsically parallel.

这取决于你程序的性质。在本质上连续的问题并不能通过添加更多Go程来提高速度。 只有当问题在本质上并行的时候,并发才能编程并行处理。

In practical terms, programs that spend more time communicating on channels than doing computation will experience performance degradation when using multiple OS threads. This is because sending data between threads involves switching contexts, which has significant cost. For instance, the prime sieve example from the Go specification has no significant parallelism although it launches many goroutines; increasing GOMAXPROCS is more likely to slow it down than to speed it up.

在实际应用中,比起进行运算,在信道上花费更多时间通信的程序,会在使用多操作系统线程时出现性能下降。 这是因为在线程间发送数据涉及到切换上下文,这需要很大的代价。比如说,在Go语言规范中 素数筛的例子并没有明显的并行性, 尽管它启动了一些Go程,但增加 GOMAXPROCS 更有可能会减慢速度,而非提高速度。

Go's goroutine scheduler is not as good as it needs to be. In the future, it should recognize such cases and optimize its use of OS threads. For now, GOMAXPROCS should be set on a per-application basis.

Go的Go程调度并不如所需要的那么好。在将来,它应当能识别这类情况,并优化它对操作系统线程的使用。 现在,GOMAXPROCS 应当根据每个应用来进行设置。

For more detail on this topic see the talk entitled, Concurrency is not Parallelism.

关于此话题的更多详情见 并发不是并行

Functions and Methods

函数与方法

Why do T and *T have different method sets?

为什么T与*T拥有不同的方法集?

From the Go Spec:

The method set of any other named type T consists of all methods with receiver type T. The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T).

根据Go规范中的定义

其它任意已命名类型 T 的方法集由所有带接收者类型 T 的方法组成。 与指针类型 *T 相应的方法集为所有带接收者 *TT 的方法的集(就是说,它也包含 T 的方法集)。

If an interface value contains a pointer *T, a method call can obtain a value by dereferencing the pointer, but if an interface value contains a value T, there is no useful way for a method call to obtain a pointer.

如果一个接口值包含一个指针 *T,一个方法调用可通过解引用该指针来获得一个值, 但如果一个接口值包含一个值 T,就没有可用的方式让一个方法调用获得一个指针。

Even in cases where the compiler could take the address of a value to pass to the method, if the method modifies the value the changes will be lost in the caller. As a common example, this code:

即便在编译器可以获得传入方法的值的地址的情况下,若该方法修改了该值,则更改会在调用者中丢失。 一个常见的例子是,代码:

var buf bytes.Buffer
io.Copy(buf, os.Stdin)

would copy standard input into a copy of buf, not into buf itself. This is almost never the desired behavior.

会将标准输入复制到 buf副本中,而不是复制到 buf 自身。这几乎是从不期望的行为。

What happens with closures running as goroutines?

闭包作为Go程在运行时会发生什么?

Some confusion may arise when using closures with concurrency. Consider the following program:

当闭包与并发一起使用时,可能会产生一些混乱。考虑以下程序:

func main() {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    for _, v := range values {
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}
func main() {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    for _, v := range values {
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }

    // 在退出前等待所有Go程完成
    for _ = range values {
        <-done
    }
}

One might mistakenly expect to see a, b, c as the output. What you'll probably see instead is c, c, c. This is because each iteration of the loop uses the same instance of the variable v, so each closure shares that single variable. When the closure runs, it prints the value of v at the time fmt.Println is executed, but v may have been modified since the goroutine was launched. To help detect this and other problems before they happen, run go vet.

有人可能会错误地希望看到 a, b, c 作为输出。而你可能会看到 c, c, c。这是因为每一次循环迭代中都使用了变量 v 的相同实例,因此每一个闭包都共享了单一的变量。当该闭包运行时,它将在 fmt.Println 执行后打印出 v 的值,但 v 可能已经在Go程启动后被修改了。要在这类问题发生前发现它们,请运行 go vet

To bind the current value of v to each closure as it is launched, one must modify the inner loop to create a new variable each iteration. One way is to pass the variable as an argument to the closure:

要将 v 的当前值在每一个闭包启动后绑定至它们,就必须在每一次迭代中, 通过修改内部循环来创建新的变量。其中一种方式就是将变量作为实参传至该闭包中:

    for _, v := range values {
        go func(u string) {
            fmt.Println(u)
            done <- true
        }(v)
    }

In this example, the value of v is passed as an argument to the anonymous function. That value is then accessible inside the function as the variable u.

在这个例子中,v 的值作为一个实参传入了该匿名函数。然后这个值就可作为变量 u 在该函数中访问了。

Even easier is just to create a new variable, using a declaration style that may seem odd but works fine in Go:

甚至只需简单地创建新的变量,使用声明的风格看起来可能有点怪,但这在Go中能很好地工作:

    for _, v := range values {
        v := v // create a new 'v'.
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }
    for _, v := range values {
        v := v // 创建新的“v”。
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }

Control flow

流程控制

Does Go have the ?: operator?

Go有没有 ?: 操作符?

There is no ternary form in Go. You may use the following to achieve the same result:

在Go中没有三元操作符的形式。你可以使用下面的方法来识相相同的结果:

if expr {
    n = trueVal
} else {
    n = falseVal
}

Packages and Testing

包与测试

How do I create a multifile package?

我如何创建多文件包?

Put all the source files for the package in a directory by themselves. Source files can refer to items from different files at will; there is no need for forward declarations or a header file.

把所有源文件都放进与它们自己的包相同的目录中去。源文件可随意从不同的文件中引用项, 而无需提前声明或头文件。

Other than being split into multiple files, the package will compile and test just like a single-file package.

除分割成多个文件外,包可以像个单文件包一样编译并测试。

How do I write a unit test?

我如何编写单元测试?

Create a new file ending in _test.go in the same directory as your package sources. Inside that file, import "testing" and write functions of the form

在相同的目录中创建一个以 _test.go 结尾的新文件作为你的包源文件。 在该文件中,加入 import "testing" 并编写以下形式的函数:

func TestFoo(t *testing.T) {
    ...
}

Run go test in that directory. That script finds the Test functions, builds a test binary, and runs it.

在该目录中运行 go test。该脚本会查找 Test 函数, 构建一个测试二进制文件并运行它。

See the How to Write Go Code document, the testing package and the go test subcommand for more details.

更多详情见如何使用Go编程文档、 testing 包以及 go test子命令。

Where is my favorite helper function for testing?

我最喜欢的测试助手函数在哪?

Go's standard testing package makes it easy to write unit tests, but it lacks features provided in other language's testing frameworks such as assertion functions. An earlier section of this document explained why Go doesn't have assertions, and the same arguments apply to the use of assert in tests. Proper error handling means letting other tests run after one has failed, so that the person debugging the failure gets a complete picture of what is wrong. It is more useful for a test to report that isPrime gives the wrong answer for 2, 3, 5, and 7 (or for 2, 4, 8, and 16) than to report that isPrime gives the wrong answer for 2 and therefore no more tests were run. The programmer who triggers the test failure may not be familiar with the code that fails. Time invested writing a good error message now pays off later when the test breaks.

Go的标准 testing 包使编写单元测试更加容易, 但是它缺乏在其它语言的测试框架提供的特性,比如断言函数。 在本文档前面的一小节中解释了为什么Go没有断言, 同样的论点也适用于在测试中对 assert 的使用。 适当的错误处理意味着可以在一项测试失败后让其它测试继续运行, 因此调试错误的人能够得到一幅关于错误的完整的画面。比起 isPrime 对于2给出错误答案的报告后就不再运行更多测试来说,isPrime 对于2、3、5和7(或2、4、8和16)给出错误答案的报告更加有用。 触发测试失败的程序员可以无需熟悉失败的代码。

A related point is that testing frameworks tend to develop into mini-languages of their own, with conditionals and controls and printing mechanisms, but Go already has all those capabilities; why recreate them? We'd rather write tests in Go; it's one fewer language to learn and the approach keeps the tests straightforward and easy to understand.

一个相关的问题是,测试框架往往会发展成他们自己的带条件测试、流程控制以及打印机制的迷你语言, 但Go已经拥有了所有的那些能力,为什么要重新创造它们呢?我们更愿意在Go中写测试, 因为它只需学习少量的语言,而这种方式会使测试直截了当且易于理解。

If the amount of extra code required to write good errors seems repetitive and overwhelming, the test might work better if table-driven, iterating over a list of inputs and outputs defined in a data structure (Go has excellent support for data structure literals). The work to write a good test and good error messages will then be amortized over many test cases. The standard Go library is full of illustrative examples, such as in the formatting tests for the fmt package.

如果需要编写额外的代码量,良好的错误似乎是重复的和压倒一切的,如果通过表格控制, 在数据结构中定义的输入输出列表上进行迭代(Go对于数据结构字面有着极好的支持), 则该测试可能会工作得更好。编写良好的测试及良好的错误信息的工作会分担很多测试情况。 在Go的标准库中充满了说明性的例子,比如说在 fmt 包中的格式化测试

Implementation

实现

What compiler technology is used to build the compilers?

该编译器使用什么编译器技术构建?

Gccgo has a front end written in C++, with a recursive descent parser coupled to the standard GCC back end. Gc is written in C using yacc/bison for the parser. Although it's a new program, it fits in the Plan 9 C compiler suite (http://plan9.bell-labs.com/sys/doc/compiler.html) and uses a variant of the Plan 9 loader to generate ELF/Mach-O/PE binaries.

gccgo 拥有一个耦合到标准GCC后端的,带递归下降解析器的C++前端。 Gc 是用C编写的,它使用 yacc/bison 作为解析器。 尽管它是个新的程序,但它也适用于Plan 9的C编译器套件( http://plan9.bell-labs.com/sys/doc/compiler.html)并使用了Plan 9加载程序的一种变体来生成ELF/Mach-O/PE二进制文件。

We considered using LLVM for gc but we felt it was too large and slow to meet our performance goals.

We also considered writing gc, the original Go compiler, in Go itself but elected not to do so because of the difficulties of bootstrapping and especially of open source distribution—you'd need a Go compiler to set up a Go environment. Gccgo, which came later, makes it possible to consider writing a compiler in Go. A plan to do that by machine translation of the existing compiler is under development. A separate document explains the reason for this approach.

我们考虑过用Go自身编写官方的Go编译器 gc,但由于自举的困难和开源分布的特殊性, 我们决定不这样做——你需要一个Go编译器来设置一个Go的环境。gccgo 出现得稍晚一些,它让考虑使用Go来编写编译器成为了可能,这很有可能发生。 (Go是实现编译器的不错的语言;原生的词法分析器和解析器在 go 包中已经可用,类型检查器正在开发。)

That plan aside, Go is a fine language in which to implement a self-hosting compiler: a native lexer and parser are already available in the go package and a separate type checking package has also been written.

我们也考虑过为 gc 使用LLVM,但我们认为它过于庞大且慢得难以满足我们的性能目标。

How is the run-time support implemented?

运行时如何支持实现?

Again due to bootstrapping issues, the run-time code was originally written mostly in C (with a tiny bit of assembler) although much of it has been translated to Go since then and one day all of it might be (except for the assembler bits). Gccgo's run-time support uses glibc. Gc uses a custom C library to keep the footprint under control; it is compiled with a version of the Plan 9 C compiler that supports resizable stacks for goroutines. The gccgo compiler implements these on Linux only, using a technique called segmented stacks, supported by recent modifications to the gold linker.

还是因为自举的问题,运行时代码大部分以C编写(以及一丁点汇编), 尽管现在Go已经能实现它的大部分功能。gccgo 的运行时使用 glibc 支持。gc 使用了一个定制的库以保证它的封装在控制之下; 它使用了Plan 9 C编译器的一个为Go程支持分段栈的版本进行编译。gccgo 编译器只在Linux上实现了分段栈,由gold连接器最近的修改所支持。

Why is my trivial program such a large binary?

为什么我琐碎的程序其二进制文件却如此之大?

The linkers in the gc tool chain (5l, 6l, and 8l) do static linking. All Go binaries therefore include the Go run-time, along with the run-time type information necessary to support dynamic type checks, reflection, and even panic-time stack traces.

gc工具链(5l6l 以及 8l)中的连接器做静态链接。 因此所有的Go二进制文件都包括了Go运行时,连同运行时类型信息必须支持的动态类型检测、 反射甚至恐慌时栈跟踪。

A simple C "hello, world" program compiled and linked statically using gcc on Linux is around 750 kB, including an implementation of printf. An equivalent Go program using fmt.Printf is around 1.9 MB, but that includes more powerful run-time support and type information.

一个简单的C“hello, world”程序在Linux上使用gcc静态地编译并连接后大约有750KB, 包括一个 printf 的实现。一个使用 fmt.Printf 的等价的Go程序大约有1.2MB,但它包含更多强大的运行时支持。

Can I stop these complaints about my unused variable/import?

我能否停止关于我未使用变量/导入的抱怨?

The presence of an unused variable may indicate a bug, while unused imports just slow down compilation, an effect that can become substantial as a program accumulates code and programmers over time. For these reasons, Go refuses to compile programs with unused variables or imports, trading short-term convenience for long-term build speed and program clarity.

未使用变量的存在可能预示着bug,而未使用的导入只会减慢编译速度。 在你的代码树中积累太多的未使用导入可能会使事情变得非常慢。 由于这些原因,Go都不允许它们出现。

Still, when developing code, it's common to create these situations temporarily and it can be annoying to have to edit them out before the program will compile.

在开发代码时,临时创建这些状况很常见,而在程序编译之前必须将它们编辑掉是很烦人的。

Some have asked for a compiler option to turn those checks off or at least reduce them to warnings. Such an option has not been added, though, because compiler options should not affect the semantics of the language and because the Go compiler does not report warnings, only errors that prevent compilation.

有些人要求加入一个编译器选项来关闭这些检查,或至少减少那些警告。 然而,这样的选项还未被添加,因为编译器选项不能影响到语言的语义, 而且Go编译器并不报告警告,只会报告错误来防止编译。

There are two reasons for having no warnings. First, if it's worth complaining about, it's worth fixing in the code. (And if it's not worth fixing, it's not worth mentioning.) Second, having the compiler generate warnings encourages the implementation to warn about weak cases that can make compilation noisy, masking real errors that should be fixed.

没有警告的理由有两个。其一,若它值得抱怨,也就值得在代码中修复它。(而如果它不值得修复, 也就没必要提到。)其二,让编译器产生警告会鼓励实现就微弱的情况产生警告,这会使编译器变得嘈杂, 从而掩盖那些需要被修复的真正的错误。

It's easy to address the situation, though. Use the blank identifier to let unused things persist while you're developing.

尽管解说这些情况是容易的,然而可以使用空白标识符来让未使用的东西在你的开发中存在一会儿。

import "unused"

// This declaration marks the import as used by referencing an
// item from the package.
var _ = unused.Item  // TODO: Delete before committing!

func main() {
    debugData := debug.Profile()
    _ = debugData // Used only during debugging.
    ....
}
import "unused"

// 此声明标记了从包中导入的被引用的项。
var _ = unused.Item  // TODO:在提交前删除它!

func main() {
    debugData := debug.Profile()
    _ = debugData // 只在调试时使用
    ....
}

Nowadays, most Go programmers use a tool, goimports, which automatically rewrites a Go source file to have the correct imports, eliminating the unused imports issue in practice. This program is easily connected to most editors to run automatically when a Go source file is written.

现在,大多数 Go 程序员会使用 goimports 工具,它能自动重写 Go 源文件使其拥有正确的导入,消除实践中的未使用导入问题。 此程序很容易连接到大多数编辑器,使其在保存 Go 源文件时自动运行。

Performance

性能

Why does Go perform badly on benchmark X?

为什么Go在基准测试X中表现很差?

One of Go's design goals is to approach the performance of C for comparable programs, yet on some benchmarks it does quite poorly, including several in test/bench/shootout. The slowest depend on libraries for which versions of comparable performance are not available in Go. For instance, pidigits.go depends on a multi-precision math package, and the C versions, unlike Go's, use GMP (which is written in optimized assembler). Benchmarks that depend on regular expressions (regex-dna.go, for instance) are essentially comparing Go's native regexp package to mature, highly optimized regular expression libraries like PCRE.

Go的设计目标之一就是在可比较的程序上逼近C的性能,然而在一些基准测试中它的表现确实很差, 包括几项test/bench/shootout中的测试。 最慢的依赖库对于可比较性能的版本来说在Go中并不可用。例如 pidigits.go 依赖于一个多精度的数学包,而C版本的则使用GMP (它使用优化的汇编编写的)。依赖于正则表达式的基准测试 (例如regex-dna.go) 在本质上是将Go的原生regexp包与像PCRE那样成熟的, 高度优化的正则表达式库相比较。

Benchmark games are won by extensive tuning and the Go versions of most of the benchmarks need attention. If you measure comparable C and Go programs (reverse-complement.go is one example), you'll see the two languages are much closer in raw performance than this suite would indicate.

虽然基准测试游戏通过广泛的调优赢了,但大部分Go版本的基准测试还需要关注。如果你考量了C和Go的可比较程序 (reverse-complement.go是其中一个例子), 你就会发现这两种语言在这个套件上表明的原始性能非常接近。

Still, there is room for improvement. The compilers are good but could be better, many libraries need major performance work, and the garbage collector isn't fast enough yet. (Even if it were, taking care not to generate unnecessary garbage can have a huge effect.)

不过,它还有提升的空间。编译器很好,但可以变得更好,一些库需要主要的性能工作, 且垃圾回收器也还不够快。(即使这样,也要小心不要产生不必要的垃圾,否则会有巨大的影响。)

In any case, Go can often be very competitive. There has been significant improvement in the performance of many programs as the language and tools have developed. See the blog post about profiling Go programs for an informative example.

在任何情况下,Go都是非常有竞争力的。用该语言及工具开发的许多软件在性能上都有着明显的改善。 请参阅博文Go程序性能分析 了解一个有益的例子。

Changes from C

对于C的改变

Why is the syntax so different from C?

为什么它的语法和C如此不同?

Other than declaration syntax, the differences are not major and stem from two desires. First, the syntax should feel light, without too many mandatory keywords, repetition, or arcana. Second, the language has been designed to be easy to analyze and can be parsed without a symbol table. This makes it much easier to build tools such as debuggers, dependency analyzers, automated documentation extractors, IDE plug-ins, and so on. C and its descendants are notoriously difficult in this regard.

除了声明语法外,它们之间的不同并不多,这主要源于两种需求。首先,语法应当感觉很轻量, 没有太多强制性的关键字、重复或奥秘。其次,该语言被设计成易于分析的,无需符号表来解析。 这会使构建诸如调试器、依赖分析器、自动文档提取器、IDE插件等工具变得更加容易。 C及其后代在这方面上是极其困难的。

Why are declarations backwards?

为什么声明在后面?

They're only backwards if you're used to C. In C, the notion is that a variable is declared like an expression denoting its type, which is a nice idea, but the type and expression grammars don't mix very well and the results can be confusing; consider function pointers. Go mostly separates expression and type syntax and that simplifies things (using prefix * for pointers is an exception that proves the rule). In C, the declaration

如果你习惯于C,对你来说它们只是在后面而已。在C中,它的概念就像用表示它类型的表达式来声明变量。 这是个好主意,不过类型和表达式的语法不要混合得太好,而结果会使人迷惑;考虑函数指针。 Go将表达式和类型语法大部分分离开,并简化了一些东西(对指针使用 * 前缀是检验该规则的例外)。 在C中,声明

    int* a, b;

declares a to be a pointer but not b; in Go

会将 a 声明为指针,而 b 则不会;而在Go中

    var a, b *int

declares both to be pointers. This is clearer and more regular. Also, the := short declaration form argues that a full variable declaration should present the same order as := so

会将二者都声明为指针。这样更清楚也更规则。另外,:= 短变量声明形式证明一个完整的变量声明应当以 := 呈现相同的顺序,因此

    var a uint64 = 1

has the same effect as

的效果等同于

    a := uint64(1)

Parsing is also simplified by having a distinct grammar for types that is not just the expression grammar; keywords such as func and chan keep things clear.

解析也通过拥有一个独特的,不只是表达式的类型语法而得到了简化,像 funcchan 这样的关键字让事情变得清晰。

See the article about Go's Declaration Syntax for more details.

更多详情请参阅Go的声明语法

Why is there no pointer arithmetic?

为什么没有指针运算?

Safety. Without pointer arithmetic it's possible to create a language that can never derive an illegal address that succeeds incorrectly. Compiler and hardware technology have advanced to the point where a loop using array indices can be as efficient as a loop using pointer arithmetic. Also, the lack of pointer arithmetic can simplify the implementation of the garbage collector.

为了安全。没有指针运算可创建一种语言,它不会派生出可以错误地成功访问的非法地址。 编译器和硬件技术已经发展到了循环使用数组下标比循环使用指针运算更有效率的地步。 此外,指针运算的缺失还可以简化垃圾收集器的实现。

Why are ++ and -- statements and not expressions? And why postfix, not prefix?

为什么 ++-- 语句不是表达式?为什么只有后缀式而没有前缀式?

Without pointer arithmetic, the convenience value of pre- and postfix increment operators drops. By removing them from the expression hierarchy altogether, expression syntax is simplified and the messy issues around order of evaluation of ++ and -- (consider f(i++) and p[i] = q[++i]) are eliminated as well. The simplification is significant. As for postfix vs. prefix, either would work fine but the postfix version is more traditional; insistence on prefix arose with the STL, a library for a language whose name contains, ironically, a postfix increment.

没有指针运算,前缀和后缀增量操作符的便利性就会减少。通过将它们从表达式层级中整体移除, 表达式语法就会简化,而围绕 ++-- 求值顺序混乱的问题 (考虑 f(i++)p[i] = q[++i])就能被很好地消除。 这种简化是非常有意义的。至于后缀式与前缀式,二者都能很好地工作,但后缀式版本更加传统; 前缀式则是STL所坚持的。具有讽刺意味的是,具有讽刺意味的是,它是为某种名字里包含后缀增量的语言写的。

Why are there braces but no semicolons? And why can't I put the opening brace on the next line?

为什么有大括号却没有分号?为什么我不能将开大括号放在下一行?

Go uses brace brackets for statement grouping, a syntax familiar to programmers who have worked with any language in the C family. Semicolons, however, are for parsers, not for people, and we wanted to eliminate them as much as possible. To achieve this goal, Go borrows a trick from BCPL: the semicolons that separate statements are in the formal grammar but are injected automatically, without lookahead, by the lexer at the end of any line that could be the end of a statement. This works very well in practice but has the effect that it forces a brace style. For instance, the opening brace of a function cannot appear on a line by itself.

Go使用大括号为语句进行分组,这种语法对于使用C家族中任何语言工作的程序员来说是很熟悉的。 然而,分号是为了解析器,而不是人们,而我们想要尽可能地消除它。为实现这个目标, Go从BCPL里借鉴了一个小诡计:用来分隔语句的分号还在正式的语法中,但词法分析器将所有行末都当做语句的结束, 并自动插入分号,而无需前瞻。这种做法在实践中非常好,不过副作用就是强制的大括号风格。 例如,函数的开大括号不能单独占据一行。

Some have argued that the lexer should do lookahead to permit the brace to live on the next line. We disagree. Since Go code is meant to be formatted automatically by gofmt, some style must be chosen. That style may differ from what you've used in C or Java, but Go is a new language and gofmt's style is as good as any other. More important—much more important—the advantages of a single, programmatically mandated format for all Go programs greatly outweigh any perceived disadvantages of the particular style. Note too that Go's style means that an interactive implementation of Go can use the standard syntax one line at a time without special rules.

一些人争论词法分析器应当前瞻性地允许大括号占据下一行。而我们不这么认为。 由于Go代码会自动地被gofmt 格式化, 一些风格就必须被选择。那种风格可能不同于你在C或Java中使用的风格, 但Go是一门新的语言,而且 gofmt 的风格比任何其它的风格都要好。 更重要——还要重要的是,对于所有的Go程序来说,单一的、程序化的、强制性格式的优势, 比任何独有的风格的劣势都更加好。还需要注意的是, Go的风格意味着Go的交互式实现可以使用标准的的语法,一次一行而无需特殊的规则。

Why do garbage collection? Won't it be too expensive?

为什么要有垃圾回收?代价会不会太高?

One of the biggest sources of bookkeeping in systems programs is memory management. We feel it's critical to eliminate that programmer overhead, and advances in garbage collection technology in the last few years give us confidence that we can implement it with low enough overhead and no significant latency.

记账式系统编程的最大来源就是内存管理。我们觉得关键就在于消除程序员的开销, 而垃圾回收技术的进步给了我们以足够低的开销和没有明显的延迟来实现它的信心。

Another point is that a large part of the difficulty of concurrent and multi-threaded programming is memory management; as objects get passed among threads it becomes cumbersome to guarantee they become freed safely. Automatic garbage collection makes concurrent code far easier to write. Of course, implementing garbage collection in a concurrent environment is itself a challenge, but meeting it once rather than in every program helps everyone.

另一点是并发和多线程编程的一大部分困难也源于内存管理; 在线程之间传递的对象要保证它们被安全地释放是很麻烦的。自动垃圾回收使并发代码很容易编写。 当然,在并发环境中实现垃圾回收本身也是一个挑战,但比起在每个程序中实现它来说, 只需实现它一次就能帮助到每一个人。

Finally, concurrency aside, garbage collection makes interfaces simpler because they don't need to specify how memory is managed across them.

最后,撇开并发不说,垃圾回收使接口更简单,因为它们无需指定内存该如何管理。

The current implementation is a parallel mark-and-sweep collector but a future version might take a different approach.

当前实现是并行的标记并清理试收集器,但将来的版本可能会使用不同的方法。

On the topic of performance, keep in mind that Go gives the programmer considerable control over memory layout and allocation, much more than is typical in garbage-collected languages. A careful programmer can reduce the garbage collection overhead dramatically by using the language well; see the article about profiling Go programs for a worked example, including a demonstration of Go's profiling tools.

关于性能的话题,只需记住Go在内存布局和分配上给了程序员相当大的控制权,比典型的垃圾回收式语言更多。 细心的程序员通过好好使用该语言,可以显著减少垃圾收集的开销; 请参阅关于Go程序性能分析的文章, 里面包含了一个可工作的例子和一个Go分析工具的演示。