Frequently Asked Questions (FAQ) 常见问题解答(FAQ)

Origins 起源

What is the purpose of the project? 该项目的目的是什么?

At the time of Go's inception, only a decade ago, the programming world was different from today. 在Go诞生之初(仅十年前),编程世界与今天有所不同. Production software was usually written in C++ or Java, GitHub did not exist, most computers were not yet multiprocessors, and other than Visual Studio and Eclipse there were few IDEs or other high-level tools available at all, let alone for free on the Internet. 生产软件通常是用C ++或Java编写的,GitHub不存在,大多数计算机还没有多处理器,除了Visual Studio和Eclipse,几乎没有可用的IDE或其他高级工具,更不用说在互联网上免费了.

Meanwhile, we had become frustrated by the undue complexity required to use the languages we worked with to develop server software. 同时,我们对于使用与我们一起开发服务器软件所使用的语言所需的过度复杂性感到沮丧. Computers had become enormously quicker since languages such as C, C++ and Java were first developed but the act of programming had not itself advanced nearly as much. 自从首先开发诸如C,C ++和Java之类的语言以来,计算机已经变得非常快,但是编程本身并没有进步太多. Also, it was clear that multiprocessors were becoming universal but most languages offered little help to program them efficiently and safely. 同样,很明显,多处理器正在变得普遍,但是大多数语言对有效和安全地对其进行编程几乎没有帮助.

We decided to take a step back and think about what major issues were going to dominate software engineering in the years ahead as technology developed, and how a new language might help address them. 我们决定退后一步,考虑随着技术的发展,未来几年将在软件工程中占据主导地位的主要问题,以及一种新的语言如何帮助解决这些问题. For instance, the rise of multicore CPUs argued that a language should provide first-class support for some sort of concurrency or parallelism. 例如,多核CPU的兴起表明,一种语言应为某种并发性或并行性提供一流的支持. And to make resource management tractable in a large concurrent program, garbage collection, or at least some sort of safe automatic memory management was required. 为了使资源管理在大型并发程序中易于处理,需要垃圾回收或至少某种安全的自动内存管理.

These considerations led to a series of discussions from which Go arose, first as a set of ideas and desiderata, then as a language. 这些考虑引发了一系列讨论 ,Go产生了一系列讨论 ,首先是作为一组想法和渴望,然后是一种语言. An overarching goal was that Go do more to help the working programmer by enabling tooling, automating mundane tasks such as code formatting, and removing obstacles to working on large code bases. 一个总体目标是,Go可以通过启用工具,自动执行诸如代码格式化之类的平凡任务以及消除在大型代码库上工作的障碍,来为工作中的程序员提供更多帮助.

A much more expansive description of the goals of Go and how they are met, or at least approached, is available in the article, Go at Google: Language Design in the Service of Software Engineering . 在Google的Go语言:软件工程服务中的语言设计中 ,可以找到关于Go语言的目标以及如何实现或至少达到目标的更广泛的描述.

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. Robert Griesemer,Rob Pike和Ken Thompson于2007年9月21日开始在白板上草拟一种新语言的目标.几天之内,这些目标就已经定下来,成为了做某事的计划以及对它的想法. Design continued part-time in parallel with unrelated work. 继续进行兼职工作,同时进行无关的工作. By January 2008, Ken had started work on a compiler with which to explore ideas; 到2008年1月,Ken开始研究一种编译器,用于探索思想. it generated C code as its output. 它生成C代码作为其输出. 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. 2008年5月,伊恩·泰勒(Ian Taylor)使用规范草案独立地开始了Go的GCC前端. Russ Cox joined in late 2008 and helped move the language and libraries from prototype to reality. 拉斯·考克斯(Russ Cox)于2008年末加入,并帮助将语言和库从原型转变为现实.

Go became a public open source project on November 10, 2009. Countless people from the community have contributed ideas, discussions, and code. Go于2009年11月10日成为一个公共开源项目.社区中无数的人贡献了想法,讨论和代码.

There are now millions of Go programmers—gophers—around the world, and there are more every day. 现在,全球有数以百万计的Go程序员(专家),而且每天都有更多的程序员. Go's success has far exceeded our expectations. Go的成功远远超出了我们的期望.

What's the origin of the gopher mascot? 地鼠吉祥物的起源是什么?

The mascot and logo were designed by Renée French , who also designed Glenda , the Plan 9 bunny. 吉祥物和徽标由RenéeFrench设计,后者还设计了Plan 9兔子Glenda . A blog post about the gopher explains how it was derived from one she used for a WFMU T-shirt design some years ago. 关于地鼠的博客文章解释了它是如何从几年前她用来设计WFMU T恤的人衍生而来的. The logo and mascot are covered by the Creative Commons Attribution 3.0 license. 徽标和吉祥物受知识共享署名3.0许可的保护.

The gopher has a model sheet illustrating his characteristics and how to represent them correctly. 地鼠的模型表说明了他的特征以及如何正确表示它们. The model sheet was first shown in a talk by Renée at Gophercon in 2016. He has unique features; 该模型表首先在Renée在Gophercon的2016年的一次演讲中展示. he's the Go gopher , not just any old gopher. 他是Go gopher ,而不仅仅是任何老地鼠.

Is the language called Go or Golang? 语言是Go还是Golang?

The language is called Go. 该语言称为Go. The "golang" moniker arose because the web site is golang.org , not go.org, which was not available to us. 该"golang"绰号的出现是因为该网站是golang.org ,不go.org,这是不提供给我们. Many use the golang name, though, and it is handy as a label. 但是,许多人都使用golang名称,它很容易用作标签. For instance, the Twitter tag for the language is "#golang". 例如,该语言的Twitter标记是" #golang". The language's name is just plain Go, regardless. 不管怎样,该语言的名称只是普通的Go.

A side note: Although the official logo has two capital letters, the language name is written Go, not GO. 旁注:尽管官方徽标有两个大写字母,但语言名称写为Go,而不是GO.

Why did you create a new language? 您为什么要创建一种新语言?

Go was born out of frustration with existing languages and environments for the work we were doing at Google. Go诞生于对我们在Google上所做的工作的现有语言和环境的沮丧. 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. 选择使用动态类型化的语言(例如Python和JavaScript)而不是C ++或在较小程度上使用Java的程序员可能会在安全性和效率上放轻松.

We were not alone in our concerns. 我们并不孤单. After many years with a pretty quiet landscape for programming languages, Go was among the first of several new languages—Rust, Elixir, Swift, and more—that have made programming language development an active, almost mainstream field again. 经过多年的编程语言一片平静之后,Go成为了几种新语言(Rust,Elixir,Swift等)中的第一种,这些新语言使编程语言开发再次成为活跃的,几乎是主流的领域.

Go addressed these issues by attempting to combine the ease of programming of an interpreted, dynamically typed language with the efficiency and safety of a statically typed, compiled language. Go通过尝试将解释型动态类型语言的编程易用性与静态类型编译语言的效率和安全性相结合,解决了这些问题. It also aimed to be modern, with support for networked and multicore computing. 它还旨在成为现代化的,并支持网络和多核计算. Finally, working with Go is intended to be fast : it should take at most a few seconds to build a large executable on a single computer. 最后,使用Go的目的是要更快 :在单台计算机上构建大型可执行文件最多需要几秒钟. 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. 需要一种新的语言.

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. Google上的文章Go讨论了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). Go主要属于C族(基本语法),来自Pascal / Modula / Oberon族(声明,程序包)的大量投入,以及来自Tony Hoare CSP启发的语言的一些构想,例如Newsqueak和Limbo(并发). 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. 在各个方面,都通过考虑程序员的工作方式以及如何使程序设计(至少是我们所做的编程类型)更有效,更有趣的方式来设计该语言.

What are the guiding principles in the design? 设计中的指导原则是什么?

When Go was designed, Java and C++ were the most commonly used languages for writing servers, at least at Google. 在设计Go时,至少在Google来说,Java和C ++是编写服务器的最常用语言. We felt that these languages required too much bookkeeping and repetition. 我们认为这些语言需要太多的簿记和重复. Some programmers reacted by moving towards more dynamic, fluid languages like Python, at the cost of efficiency and type safety. 一些程序员的反应是,以效率和类型安全为代价,转向了更动态,流畅的语言(如Python). We felt it should be possible to have the efficiency, the safety, and the fluidity in a single language. 我们认为用一种语言就能具有效率,安全性和流动性.

Go attempts to reduce the amount of typing in both senses of the word. Go尝试减少两种词义的打字量. 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. 口吃( foo.Foo* myFoo = new(foo.Foo) )通过使用:=声明并初始化构造简单地派生而减少. 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可以表达而又易于理解,而不会牺牲复杂性.

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. 是. Go is used widely in production inside Google. Go在Google内部的生产中被广泛使用. One easy example is the server behind golang.org . 一个简单的例子是golang.org后面的服务器. It's just the godoc document server running in a production configuration on Google App Engine . 它只是在Google App Engine的生产配置中运行的godoc文档服务器.

A more significant instance is Google's download server, dl.google.com , which delivers Chrome binaries and other large installables such as apt-get packages. 一个更重要的例子是Google的下载服务器dl.google.com ,它提供了Chrome二进制文件和其他大型可安装程序,例如apt-get软件包.

Go is not the only language used at Google, far from it, but it is a key language for a number of areas including site reliability engineering (SRE) and large-scale data processing. Go并不是Google唯一使用的语言,它远非它使用,但它是许多领域的关键语言,包括站点可靠性工程(SRE)和大规模数据处理.

What other companies use Go? 还有哪些公司使用Go?

Go usage is growing worldwide, especially but by no means exclusively in the cloud computing space. Go的使用在全球范围内都在增长,特别是但绝不是在云计算领域中. A couple of major cloud infrastructure projects written in Go are Docker and Kubernetes, but there are many more. Go编写的几个主要的云基础架构项目是Docker和Kubernetes,但还有更多.

It's not just cloud, though. 但是,不仅仅是云. The Go Wiki includes a page , updated regularly, that lists some of the many companies using Go. Go Wiki包含定期更新的页面 ,该页面列出了许多使用Go的公司.

The Wiki also has a page with links to success stories about companies and projects that are using the language. Wiki也有一个页面,其中包含有关使用该语言的公司和项目的成功案例的链接.

It is possible to use C and Go together in the same address space, but it is not a natural fit and can require special interface software. 可以在相同的地址空间中一起使用C和Go,但这并不是很自然的选择,可能需要特殊的接口软件. Also, linking C with Go code gives up the memory safety and stack management properties that Go provides. 同样,将C与Go代码链接会放弃Go提供的内存安全性和堆栈管理属性. Sometimes it's absolutely necessary to use C libraries to solve a problem, but doing so always introduces an element of risk not present with pure Go code, so do so with care. 有时,绝对有必要使用C库来解决问题,但是这样做总是会引入纯Go代码所没有的风险元素,因此请务必谨慎.

If you do need to use C with Go, how to proceed depends on the Go compiler implementation. 如果确实需要将C与Go一起使用,如何进行取决于Go编译器的实现. There are three Go compiler implementations supported by the Go team. Go团队支持三种Go编译器实现. These are gc , the default compiler, gccgo , which uses the GCC back end, and a somewhat less mature gollvm , which uses the LLVM infrastructure. 它们是gc ,默认编译器gccgo使用GCC后端,而成熟gollvm较低的gollvm使用LLVM基础架构.

Gc uses a different calling convention and linker from C and therefore cannot be called directly from C programs, or vice versa. Gc使用与C不同的调用约定和链接器,因此不能直接从C程序调用,反之亦然. The cgo program provides the mechanism for a "foreign function interface" to allow safe calling of C libraries from Go code. cgo程序提供了"外部函数接口"的机制,以允许从Go代码安全地调用C库. SWIG extends this capability to C++ libraries. SWIG将此功能扩展到C ++库.

You can also use cgo and SWIG with Gccgo and gollvm . 您还可以将cgo和SWIG与Gccgogollvm . Since they use a traditional API, it's also possible, with great care, to link code from these compilers directly with GCC/LLVM-compiled C or C++ programs. 由于它们使用传统的API,因此也可以非常小心地将来自这些编译器的代码直接与GCC / LLVM编译的C或C ++程序链接. However, doing so safely requires an understanding of the calling conventions for all languages concerned, as well as concern for stack limits when calling C or C++ from Go. 但是,安全地执行此操作需要了解所有相关语言的调用约定,以及从Go调用C或C ++时要注意的堆栈限制.

What IDEs does Go support? Go支持哪些IDE?

The Go project does not include a custom IDE, but the language and libraries have been designed to make it easy to analyze source code. Go项目不包括自定义IDE,但已设计语言和库来简化源代码分析. As a consequence, most well-known editors and IDEs support Go well, either directly or through a plugin. 因此,大多数知名的编辑器和IDE都直接或通过插件支持Go良好.

The list of well-known IDEs and editors that have good Go support available includes Emacs, Vim, VSCode, Atom, Eclipse, Sublime, IntelliJ (through a custom variant called Goland), and many more. 具有良好的Go支持的知名IDE和编辑器列表包括Emacs,Vim,VSCode,Atom,Eclipse,Sublime,IntelliJ(通过名为Goland的自定义变体)等. Chances are your favorite environment is a productive one for programming in Go. 您最喜欢的环境是在Go中进行编程的高效环境.

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/ . 可从github.com/golang/protobuf/获得 .

Can I translate the Go home page into another language? 我可以将"首页"翻译成另一种语言吗?

Absolutely. 绝对. We encourage developers to make Go Language sites in their own languages. 我们鼓励开发人员以自己的语言制作Go语言网站. 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 但是,如果您选择将Google徽标或品牌添加到您的网站(它不会显示在golang.org上 ),则需要遵守www.google.com/permissions/guidelines.html上的准则

Design 设计

Does Go have a runtime? Go有运行时吗?

Go does have an extensive library, called the runtime , that is part of every Go program. Go确实有一个广泛的库,称为runtime ,它是每个Go程序的一部分. The runtime library implements garbage collection, concurrency, stack management, and other critical features of the Go language. 运行时库实现垃圾回收,并发,堆栈管理和Go语言的其他关键功能. Although it is more central to the language, Go's runtime is analogous to libc , the C library. 尽管Go语言的运行语言更为重要,但其运行时类似于C库libc .

It is important to understand, however, that Go's runtime does not include a virtual machine, such as is provided by the Java runtime. 但是,重要的是要了解,Go的运行时不包括Java运行时所提供的虚拟机. Go programs are compiled ahead of time to native machine code (or JavaScript or WebAssembly, for some variant implementations). Go程序会提前编译为本地机器代码(对于某些变体实现,则为JavaScript或WebAssembly). Thus, although the term is often used to describe the virtual environment in which a program runs, in Go the word "runtime" is just the name given to the library providing critical language services. 因此,尽管该术语通常用于描述程序在其中运行的虚拟环境,但是在Go语言中,"运行时"一词只是赋予提供关键语言服务的库的名称.

What's up with Unicode identifiers? Unicode标识符是怎么回事?

When designing Go, we wanted to make sure that it was not overly ASCII-centric, which meant extending the space of identifiers from the confines of 7-bit ASCII. 在设计Go时,我们要确保它不是以ASCII为中心的,这意味着要从7位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. Go的规则-识别符必须是Unicode定义的字母或数字-易于理解和实现,但有限制. Combining characters are excluded by design, for instance, and that excludes some languages such as Devanagari. 例如,组合字符在设计上被排除在外,并且排除了某些语言,例如梵文.

This rule has one other unfortunate consequence. 这条规则还有另一个不幸的后果. Since an exported identifier must begin with an upper-case letter, identifiers created from characters in some languages can, by definition, not be exported. 由于导出的标识符必须以大写字母开头,因此根据定义,不能导出由某些语言的字符创建的标识符. For now the only solution is to use something like X日本語 , which is clearly unsatisfactory. 目前,唯一的解决方案是使用X日本語 ,这显然不能令人满意.

Since the earliest version of the language, there has been considerable thought into how best to expand the identifier space to accommodate programmers using other native languages. 自从该语言的最早版本发布以来,人们就一直在思考如何最好地扩展标识符空间以适应使用其他本机语言的程序员. Exactly what to do remains an active topic of discussion, and a future version of the language may be more liberal in its definition of an identifier. 确切地,做什么仍然是讨论的活跃话题,并且该语言的未来版本在标识符定义方面可能会更加宽松. For instance, it might adopt some of the ideas from the Unicode organization's recommendations for identifiers. 例如,它可能会采用Unicode组织关于标识符的建议中的一些想法. Whatever happens, it must be done compatibly while preserving (or perhaps expanding) the way letter case determines visibility of identifiers, which remains one of our favorite features of Go. 无论发生什么情况,都必须在兼容(或扩展)字母大小写确定标识符可见性的方式下兼容完成,这仍然是Go最受欢迎的功能之一.

For the time being, 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. 目前,我们有一个简单的规则,可以在以后扩展而又不会破坏程序,该规则避免了肯定会因允许歧义标识符的规则而引起的错误.

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. Go的设计着眼于编程的实用性,编译的速度,概念的正交性以及对支持诸如并发和垃圾收集等功能的需求. 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. 您最喜欢的功能可能会丢失,因为它不合适,因为它影响编译速度或设计的清晰度,或者因为它会使基本系统模型变得太困难.

If it bothers you that Go is missing feature X , please forgive us and investigate the features that Go does have. 如果困扰您Go缺少功能X ,请原谅我们并调查Go确实具有的功能. You might find that they compensate in interesting ways for the lack of X . 您可能会发现它们以有趣的方式补偿了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. 尽管我们了解一些程序员,但我们对他们并不感到紧迫.

Go was intended as a language for writing server programs that would be easy to maintain over time. Go旨在作为一种编写服务器程序的语言,随着时间的推移该服务器程序将易于维护. (See this article for more background.) The design concentrated on things like scalability, readability, and concurrency. (有关更多背景信息,请参阅本文 .)设计集中在可伸缩性,可读性和并发性等方面. Polymorphic programming did not seem essential to the language's goals at the time, and so was left out for simplicity. 当时,多态编程对于该语言的目标似乎并不重要,因此为简单起见而被省略.

The language is more mature now, and there is scope to consider some form of generic programming. 该语言现在更加成熟,可以考虑某种形式的通用编程. However, there remain some caveats. 但是,仍然存在一些警告.

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的内置映射和切片,以及使用空接口构造容器的能力(带有显式拆箱),意味着在许多情况下,即使不那么顺利,也可以编写能够实现泛型的代码.

The topic remains open. 该主题保持打开状态. For a look at several previous unsuccessful attempts to design a good generics solution for Go, see this proposal . 要查看以前为Go设计良好的泛型解决方案的失败尝试,请参阅此建议 .

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. 我们认为,将异常耦合到控制结构(如try-catch-finally习语中)会导致代码混乱. It also tends to encourage programmers to label too many ordinary errors, such as failing to open a file, as exceptional. 它还倾向于鼓励程序员将太多的常见错误(例如,无法打开文件)标记为例外.

Go takes a different approach. Go采用了不同的方法. For plain error handling, Go's multi-value returns make it easy to report an error without overloading the return value. 对于简单的错误处理,Go的多值返回使报告错误变得容易,而不会使返回值过载. A canonical error type, coupled with Go's other features , makes error handling pleasant but quite different from that in other languages. 规范错误类型与Go的其他功能一起使错误处理令人愉悦,但与其他语言完全不同.

Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. Go还具有一些内置功能,可以发出信号并从真正异常的状态中恢复. 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. 恢复机制仅在发生错误后被破坏的功能状态的一部分中执行,该机制足以处理灾难,但不需要额外的控制结构,如果使用得当,可以生成干净的错误处理代码.

See the Defer, Panic, and Recover article for details. 有关详细信息请参见延迟,紧急和恢复文章. Also, the Errors are values blog post describes one approach to handling errors cleanly in Go by demonstrating that, since errors are just values, the full power of Go can deployed in error handling. 另外," 错误就是价值"博客文章通过证明由于错误只是值而明确描述了一种在Go中清晰处理错误的方法,因此Go的全部功能都可以部署在错误处理中.

Why does Go not have assertions? 为什么Go没有断言?

Go doesn't provide assertions. Go不提供断言. 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 to operate instead of crashing after a non-fatal error. 正确的错误处理意味着服务器可以继续运行,而不是在发生非致命错误后崩溃. 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. 当程序员看到的错误代码不熟悉时,精确的错误尤为重要.

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 over time developed 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. 我们认为,这部分归因于复杂的设计(例如pthread) ,部分归因于对低级细节(例如互斥锁,条件变量和内存屏障)的过分强调. Higher-level interfaces enable much simpler code, even if there are still mutexes and such under the covers. 更高级别的接口可以实现更简单的代码,即使幕后仍然有互斥对象等.

One of the most successful models for providing high-level linguistic support for concurrency comes from Hoare's Communicating Sequential Processes, or CSP. Hoare的通信顺序过程(CSP)是为并发提供高级语言支持的最成功模型之一. Occam and Erlang are two well known languages that stem from CSP. Occam和Erlang是源自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. Go的并发原语来自家族树的不同部分,其主要贡献是作为一流对象的强大通道概念. Experience with several earlier languages has shown that the CSP model fits well into a procedural language framework. 几种早期语言的经验表明,CSP模型非常适合过程语言框架.

Why goroutines instead of threads? 为什么使用goroutines而不是线程?

Goroutines are part of making concurrency easy to use. Goroutine是使并发易于使用的一部分. 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. 我们称之为goroutines的结果可能非常便宜:它们在堆栈内存(只有几千字节)之外的开销很小.

To make the stacks small, Go's run-time uses resizable, bounded stacks. 为了缩小堆栈,Go的运行时使用可调整大小的有界堆栈. A newly minted goroutine is given a few kilobytes, which is almost always enough. 给一个新铸造的goroutine几千字节,这几乎总是足够的. 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. 否则,运行时会增加(或缩小)用于自动存储堆栈的内存,从而使许多goroutine可以驻留在适度的内存中. The CPU overhead averages about three cheap instructions per function call. 每个函数调用的CPU开销平均约为3条廉价指令. It is practical to create hundreds of thousands of goroutines in the same address space. 在同一个地址空间中创建数十万个goroutine是很实际的. If goroutines were just threads, system resources would run out at a much smaller number. 如果goroutine只是线程,则系统资源将以更少的数量用完.

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. 经过长时间的讨论,人们决定使用地图的典型用法不需要​​从多个goroutine安全访问,并且在需要的情况下,地图可能是某些已经同步的较大数据结构或计算的一部分. 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. 但是,这并不是一个容易的决定,因为这意味着不受控制的地图访问可能会使程序崩溃.

The language does not preclude atomic map updates. 该语言不排除原子图更新. When required, such as when hosting an untrusted program, the implementation could interlock map access. 在需要时(例如在托管不受信任的程序时),实现可以互锁地图访问.

Map access is unsafe only when updates are occurring. 仅当发生更新时,地图访问才是不安全的. As long as all goroutines are only reading—looking up elements in the map, including iterating through it using a for range loop—and not changing the map by assigning to elements or doing deletions, it is safe for them to access the map concurrently without synchronization. 只要所有goroutine仅读取(在地图中查找元素,包括使用for range循环对其进行遍历),并且不通过分配元素或执行删除操作来更改地图,则它们可以安全地并发访问地图而无需同步.

As an aid to correct map use, some implementations of the language contain a special check that automatically reports at run time when a map is modified unsafely by concurrent execution. 为了更正地图的使用,该语言的某些实现包含特殊检查,当通过并发执行不安全地修改地图时,该检查会在运行时自动报告.

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, at least at the source code level (programs may need to be recompiled occasionally to stay current). 尽管Go是一个开源项目,但语言和库受到兼容性承诺的保护,该承诺可防止更改破坏现有程序,至少在源代码级别上(可能需要不时重新编译程序以保持最新). If your proposal violates the Go 1 specification we cannot even entertain the idea, regardless of its merit. 如果您的提议违反了Go 1规范,则无论其优点如何,我们都无法接受. A future major release of Go may be incompatible with Go 1, but discussions on that topic have only just begun and one thing is certain: there will be very few such incompatibilities introduced in the process. Go的未来主要发行版可能与Go 1不兼容,但是关于该主题的讨论才刚刚开始,并且可以肯定的是:在此过程中几乎不会引入这种不兼容性. Moreover, the compatibility promise encourages us to provide an automatic path forward for old programs to adapt should that situation arise. 此外,兼容性承诺鼓励我们为出现这种情况的旧程序提供自动前进的路径.

Even if your proposal is compatible with the Go 1 spec, it might not be in the spirit of Go's design goals. 即使您的建议与Go 1规范兼容,也可能不符合Go的设计目标. The article Go at Google: Language Design in the Service of Software Engineering explains Go's origins and the motivation behind its design. 《 Google上的Go:软件工程服务中的语言设计 》一文解释了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. 尽管Go具有类型和方法,并允许使用面向对象的编程风格,但没有类型层次结构. The concept of "interface" in Go provides a different approach that we believe is easy to use and in some ways more general. Go中的"接口"概念提供了一种不同的方法,我们认为该方法易于使用,并且在某些方面更通用. 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. 而且,Go中的方法比C ++或Java中的方法更通用:可以为任何类型的数据定义它们,甚至可以将内置类型(例如普通的"未装箱"整数)定义为它们. They are not restricted to structs (classes). 它们不限于结构(类).

Also, the lack of a 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. 在Go中,一个类型自动满足任何指定其方法子集的接口,而不是要求程序员提前声明两种类型相关联. 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. 因为类型和接口之间没有明确的关系,所以没有类型层次结构可以管理或讨论.

It's possible to use these ideas to construct something analogous to type-safe Unix pipes. 可以使用这些想法来构造类似于类型安全的Unix管道的东西. 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. 例如,请参阅fmt.Fprintf如何启用对任何输出(不仅是文件)的格式化打印,如何将bufio包与文件I / O完全分开,或者image包如何生成压缩的图像文件. All these ideas stem from a single interface ( io.Writer ) representing a single method ( Write ). 所有这些想法都源于表示单个方法( Write )的单个接口( io.Writer ). And that's only scratching the surface. 这只是表面. Go's interfaces have a profound influence on how programs are structured. 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和friends是因为函数在实践中是很好的,并且没有使关于基本类型的接口(从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没有"实现"声明?

A Go type satisfies an interface by implementing the methods of that interface, nothing more. Go类型通过实现该接口的方法来满足该接口,仅此而已. This property allows interfaces to be defined and used without needing 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敏捷,轻便的感觉的主要原因之一.

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 using the zero value for T or pointer to T , as appropriate: 您可以要求编译器通过尝试使用T的零值或指向T指针进行赋值来检查类型T实现了接口I

 type T struct{} 输入T struct {}
var _ I = T{} // Verify that T implements I. var _ I = T {} //验证T是否实现I.
var _ I = (*T)(nil) // Verify that *T implements I. var _ I =(* T)(nil)//验证* T是否实现I.

If T (or *T , accordingly) doesn't implement I , the mistake will be caught at compile time. 如果T (或*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 { 键入Fooer接口{
    Foo() Foo()
    ImplementsFooer() ImplementsFooer()
} }

A type must then implement the ImplementsFooer method to be a Fooer , clearly documenting the fact and announcing it in go doc 's output. 然后,类型必须将ImplementsFooer方法实现为Fooer ,清楚地记录事实并在go doc的输出中宣布该事实.

 type Bar struct{} 输入Bar struct {}
func (b Bar) ImplementsFooer() {} func(b Bar)ImplementsFooer(){}
func (b Bar) Foo() {} 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 T int型
func (t T) Equal(u T) bool { return t == u } // does not satisfy Equaler func(t T)Equal(u T)bool {return t == u} //不满足Equaler

Unlike the analogous situation in some polymorphic type systems, T does not implement Equaler . 与某些多态类型系统中的类似情况不同, T不实现Equaler . The argument type of T.Equal is T , not literally the required type Equaler . T.Equal的参数类型为T ,而不是字面上要求的Equaler类型.

In Go, the type system does not promote the argument of Equal ; 在Go中,类型系统不会提升Equal的论点; that is the programmer's responsibility, as illustrated by the type T2 , which does implement Equaler : 这是程序员的责任,如类型T2 ,它确实实现了Equaler

 type T2 int T2 int类型
func (t T2) Equal(u Equaler) bool { return t == u.(T2) } // satisfies Equaler 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 . 但是,即使这与其他类型的系统也不一样,因为在Go中,可以将满足Equaler 任何类型作为参数传递给T2.Equal ,并且在运行时必须检查参数的类型为T2 . Some languages arrange to make that guarantee at compile time. 一些语言安排在编译时做出保证.

A related example goes the other way: 一个相关的例子是相反的:

 type Opener interface { 键入Opener界面{
   Open() Reader Open()阅读器
} }

func (t T3) Open() *os.File 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的类型系统确实对程序员没有多大作用,但是缺少子类型化使得关于接口满意度的规则非常容易陈述:函数的名称和签名是否与接口的名称和签名完全相同? Go's rule is also easy to implement efficiently. Go的规则也易于有效实施. We feel these benefits offset the lack of automatic type promotion. 我们认为这些好处弥补了自动类型升级的不足. Should Go one day adopt some form of polymorphic typing, we expect there would be a way to express the idea of these examples and also have them be statically checked. 如果有一天可以采用某种形式的多态类型输入,我们希望将有一种表达这些示例思想的方法,并且可以对它们进行静态检查.

Can I convert a []T to an []interface{}? 我可以将[] T转换为[]接口{}吗?

Not directly. 不直接. It is disallowed by the language specification because the two types 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} t:= [] int {1、2、3、4}
s := make([]interface{}, len(t)) s:= make([[interface {},len(t))
for i, v := range t { 对于i,v:=范围t {
    s[i] = v s [i] = v
} }

Can I convert []T1 to []T2 if T1 and T2 have the same underlying type? 如果T1和T2具有相同的基础类型,可以将[] T1转换为[] T2吗?

This last line of this code sample does not compile. 此代码示例的最后一行不会编译.
 type T1 int T1 int类型
type T2 int T2 int类型
var t1 T1 var t1 T1
var x = T2(t1) // OK var x = T2(t1)//确定
var st1 []T1 var st1 [] T1
var sx = ([]T2)(st1) // NOT OK var sx =([] T2)(st1)//不好

In Go, types are closely tied to methods, in that every named type has a (possibly empty) method set. 在Go中,类型与方法紧密相关,因为每个命名类型都有一个(可能为空)方法集. The general rule is that you can change the name of the type being converted (and thus possibly change its method set) but you can't change the name (and method set) of elements of a composite type. 一般规则是,您可以更改要转换的类型的名称(从而可能更改其方法集),但是不能更改复合类型的元素的名称(和方法集). Go requires you to be explicit about type conversions. Go要求您明确说明类型转换.

Why is my nil error value not equal to nil? 为什么我的nil错误值不等于nil?

Under the covers, interfaces are implemented as two elements, a type T and a value V . 在幕后,接口被实现为两个元素,类型T和值V V is a concrete value such as an int , struct or pointer, never an interface itself, and has type T . V是一个具体的值,例如intstruct或指针,而不是接口本身,并且具有T类型. For instance, if we store the int value 3 in an interface, the resulting interface value has, schematically, ( T=int , V=3 ). 例如,如果我们将int值3存储在一个接口中,则结果接口值在示意图上具有( T=intV=3 ). The value V is also known as the interface's dynamic value, since a given interface variable might hold different values V (and corresponding types T ) during the execution of the program. 值V也称为接口的动态值,因为给定的接口变量在程序执行期间可能拥有不同的值V (和对应的类型T ).

An interface value is nil only if the V and T are both unset, ( T=nil , V is not set), In particular, a nil interface will always hold a nil type. 仅当VT都未设置( T=nil ,未设置V )时,接口值才为nil .特别是, nil接口将始终保持nil类型. If we store a nil pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: ( T=*int , V=nil ). 如果我们在接口值中存储类型为*intnil指针,则内部类型将为*int而与指针的值无关:( T=*intV=nil ). Such an interface value will therefore be non- nil even when the pointer value V inside is nil . 因此,这样的接口值将非nil 即使当指针的值V内是 nil .

This situation can be confusing, and arises when a nil value is stored inside an interface value such as an error return: 这种情况可能会造成混淆,并且当在接口值内存储nil值(例如error返回)时会出现这种情况:

 func returnsError() error { func returnError()错误{
	var p *MyError = nil var p * MyError = nil
	if bad() { 如果bad(){
		p = ErrBad p =错误
	} }
	return p // Will always return a non-nil error. return p //将始终返回非null错误.
} }

If all goes well, the function returns a nil p , so the return value is an error interface value holding ( T=*MyError , V=nil ). 如果一切顺利,函数将返回nil p ,因此返回值是一个error接口值保持( T=*MyErrorV=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. 这意味着如果调用者将返回的错误与nil进行比较,即使没有发生任何不好的情况,它始终看起来像是有一个错误. To return a proper nil error to the caller, the function must return an explicit nil : 要向调用者返回适当的nil error ,该函数必须返回一个明确的nil

 func returnsError() error { func returnError()错误{
	if bad() { 如果bad(){
		return ErrBad 返回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. 对于返回错误的函数总是一个好主意,即总是在签名中使用error类型(如上所述),而不是像*MyError这样的具体类型,以帮助确保正确创建错误. As an example, os.Open returns an error even though, if not nil , it's always of concrete type *os.PathError . 例如, 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 . 请记住,如果接口中已存储任何具体值,则该接口将不会为nil . For more information, see The Laws of Reflection . 有关更多信息,请参见反射定律 .

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? 为什么Go没有变体类型?

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. 我们曾考虑将变体类型添加到Go中,但是经过讨论后,他们决定将它们排除在外,因为它们与接口的混淆方式重叠. What would happen if the elements of a variant type were themselves interfaces? 如果变量类型的元素本身是接口,将会发生什么?

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. 语法树示例也是可行的,尽管不是那么优雅.

Why does Go not have covariant result types? 为什么Go没有协变结果类型?

Covariant result types would mean that an interface like 协变结果类型将意味着类似

 type Copyable interface { 类型可复制接口{
	Copy() interface{} Copy()接口{}
} }

would be satisfied by the method 将被该方法满足

 func (v Value) Copy() Value func(v值)Copy()值

because Value implements the empty interface. 因为Value实现了空接口. In Go method types must match exactly, so Value does not implement Copyable . 在Go中,方法类型必须完全匹配,因此Value不能实现Copyable . Go separates the notion of what a type does—its methods—from the type's implementation. Go将类型的作用(方法)与类型的实现分开. If two methods return different types, they are not doing the same thing. 如果两个方法返回不同的类型,则它们不会做相同的事情. Programmers who want covariant result types are often trying to express a type hierarchy through interfaces. 想要协变结果类型的程序员通常试图通过接口表达类型层次. In Go it's more natural to have a clean separation between interface and implementation. 在Go中,接口和实现之间的清晰分隔是很自然的.

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. C语言中的数字类型之间自动转换的便利性被它引起的混乱所笼罩. 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. 但是,在Go中定义常量(任意精度值没有符号和大小注释)可以大大改善问题.

A related detail is that, unlike in C, int and int64 are distinct types even if int is a 64-bit type. 一个相关的细节是,与int不同,即使int是64位类型, intint64是不同的类型. The int type is generic; int类型是泛型的; if you care about how many bits an integer holds, Go encourages you to be explicit. 如果您关心整数可以容纳多少位,那么Go鼓励您保持明确.

How do constants work in Go? 常量在Go中如何工作?

Although Go is strict about conversion between variables of different numeric types, constants in the language are much more flexible. 尽管Go严格要求在不同数值类型的变量之间进行转换,但是语言中的常量要灵活得多. Literal constants such as 23 , 3.14159 and math.Pi occupy a sort of ideal number space, with arbitrary precision and no overflow or underflow. 文字常数如233.14159math.Pi占据一种的理想数空间,以任意精度和没有溢出或下溢. For instance, the value of math.Pi is specified to 63 places in the source code, and constant expressions involving the value keep precision beyond what a float64 could hold. 例如, math.Pi的值在源代码中指定为63个位置,并且涉及该值的常量表达式保持的精度超出float64可以容纳的范围. Only when the constant or constant expression is assigned to a variable—a memory location in the program—does it become a "computer" number with the usual floating-point properties and precision. 只有将常数或常数表达式分配给变量时(程序中的存储位置),它才会成为具有常规浮点属性和精度的"计算机"数字.

Also, because they are just numbers, not typed values, constants in Go can be used more freely than variables, thereby softening some of the awkwardness around the strict conversion rules. 另外,由于它们只是数字,而不是类型化的值,因此Go中的常量比变量可以更自由地使用,从而减轻了严格转换规则周围的一些尴尬. One can write expressions such as 一个人可以写这样的表达式

 sqrt2 := math.Sqrt(2) sqrt2:= math.Sqrt(2)

without complaint from the compiler because the ideal number 2 can be converted safely and accurately to a float64 for the call to math.Sqrt .

A blog post titled 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.

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.

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.

Writing Code

How are libraries documented?

There is a program, godoc , written in Go, that extracts package documentation from the source code and serves it as a web page with links to declarations, files, and so on. An instance is running at golang.org/pkg/ . In fact, godoc implements the full site at golang.org/ .

A godoc instance may be configured to provide rich, interactive static analyses of symbols in the programs it displays; details are listed here .

For access to documentation from the command line, the go tool has a doc subcommand that provides a textual interface to the same information.

Is there a Go programming style guide?

There is no explicit style guide, although there is certainly a recognizable "Go style".

Go has established conventions to guide decisions around naming, layout, and file organization. The document Effective Go contains some advice on these topics. 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, and the vast majority in the open source world, has been run through 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.

How do I submit patches to the Go libraries?

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.

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

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

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.

Git can be configured to authenticate over HTTPS or to use SSH in place of HTTPS. To authenticate over HTTPS, you can add a line to the $HOME/.netrc file that git consults:

 machine github.com login USERNAME password APIKEY

For GitHub accounts, the password can be a personal access token .

Git can also be configured to use SSH in place of HTTPS for URLs matching a given prefix. For example, to use SSH for all GitHub access, add these lines to your ~/.gitconfig :

 [url "ssh://git@github.com/"]
	insteadOf = https://github.com/

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

Since the inception of the project, Go has had no explicit concept of package versions, but that is changing. Versioning is a source of significant complexity, especially in large code bases, and it has taken some time to develop an approach that works well at scale in a large enough variety of situations to be appropriate to supply to all Go users.

The Go 1.11 release adds new, experimental support for package versioning to the go command, in the form of Go modules. For more information, see the Go 1.11 release notes and the go command documentation .

Regardless of the actual package management technology, "go get" and the larger Go toolchain does provide 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.

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.

If you're using an externally supplied package and worry that it might change in unexpected ways, but are not yet using Go modules, the simplest solution is to copy it to your local repository. This is the approach Google takes internally and is supported by the go command through a technique called "vendoring". This involves storing a copy of the dependency under a new import path that identifies it as a local copy. See the design document for details.

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 a later section for a discussion of how this affects method receivers.)

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.

Note that this discussion is about the semantics of the operations. Actual implementations may apply optimizations to avoid copying as long as the optimizations do not change the semantics.

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 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(w, "hello, world\n")

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

 fmt.Fprintf(&w, "hello, world\n") // Compile-time error.

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.

Should I define methods on values or pointers?

 func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct) valueMethod() { } // method on value

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.

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.

By the way, in Java method receivers are always pointers, although their pointer nature is somewhat disguised (and there is a proposal to add value receivers to the language). It is the value receivers in Go that are unusual.

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.

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.

What's the difference between new and make?

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

See the relevant section of Effective Go for more details.

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

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 . On 32-bit machines the compilers use 32-bit integers by default, while on 64-bit machines integers have 64 bits. (Historically, this was not always true.)

On the other hand, floating-point scalars and complex types are always sized (there are no float or complex basic types), because programmers should be aware of precision when using floating-point numbers. The default type used for an (untyped) floating-point constant is float64 . Thus foo := 3.0 declares a variable foo of type float64 . For a float32 variable initialized by an (untyped) constant, the variable type must be specified explicitly in the variable declaration:

 var foo float32 = 3.0

Alternatively, the constant must be given a type with a conversion as in foo := float32(3.0) .

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.

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.

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?

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.

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

Concurrency

What operations are atomic? What about mutexes?

A description of the atomicity of operations in Go can be found in the Go Memory Model document.

Low-level synchronization and atomic primitives are available in the sync and sync/atomic packages. These packages are good for simple tasks such as incrementing reference counts or guaranteeing small-scale mutual exclusion.

For higher-level operations, such as coordination among concurrent servers, higher-level techniques can lead to nicer programs, and Go supports this approach through its goroutines and channels. For instance, you can structure your program so that only one goroutine at a time is ever responsible for a particular piece of data. That approach is summarized by the original Go proverb ,

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.

Large concurrent programs are likely to borrow from both these toolkits.

Why doesn't my program run faster with more CPUs?

Whether a program runs faster with more CPUs depends on the problem it is solving. The Go language provides concurrency primitives, such as goroutines and channels, but concurrency only enables parallelism when the underlying problem is intrinsically parallel. Problems that are intrinsically sequential cannot be sped up by adding more CPUs, while those that can be broken into pieces that can execute in parallel can be sped up, sometimes dramatically.

Sometimes adding more CPUs can slow a program down. In practical terms, programs that spend more time synchronizing or communicating than doing useful computation may experience performance degradation when using multiple OS threads. This is because passing data between threads involves switching contexts, which has significant cost, and that cost can increase with more CPUs. For instance, the prime sieve example from the Go specification has no significant parallelism although it launches many goroutines; increasing the number of threads (CPUs) is more likely to slow it down than to speed it up.

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

How can I control the number of CPUs?

The number of CPUs available simultaneously to executing goroutines is controlled by the GOMAXPROCS shell environment variable, whose default value is the number of CPU cores available. Programs with the potential for parallel execution should therefore achieve it by default on a multiple-CPU machine. To change the number of parallel CPUs to use, set the environment variable or use the similarly-named function of the runtime package to configure the run-time support to utilize a different number of threads. Setting it to 1 eliminates the possibility of true parallelism, forcing independent goroutines to take turns executing.

The runtime can allocate more threads than the value of GOMAXPROCS to service multiple outstanding I/O requests. GOMAXPROCS only affects how many goroutines can actually execute at once; arbitrarily more may be blocked in system calls.

Go's goroutine scheduler is not as good as it needs to be, although it has improved over time. In the future, it may better optimize its use of OS threads. For now, if there are performance issues, setting GOMAXPROCS on a per-application basis may help.

Why is there no goroutine ID?

Goroutines do not have names; they are just anonymous workers. They expose no unique identifier, name, or data structure to the programmer. Some people are surprised by this, expecting the go statement to return some item that can be used to access and control the goroutine later.

The fundamental reason goroutines are anonymous is so that the full Go language is available when programming concurrent code. By contrast, the usage patterns that develop when threads and goroutines are named can restrict what a library using them can do.

Here is an illustration of the difficulties. Once one names a goroutine and constructs a model around it, it becomes special, and one is tempted to associate all computation with that goroutine, ignoring the possibility of using multiple, possibly shared goroutines for the processing. If the net/http package associated per-request state with a goroutine, clients would be unable to use more goroutines when serving a request.

Moreover, experience with libraries such as those for graphics systems that require all processing to occur on the "main thread" has shown how awkward and limiting the approach can be when deployed in a concurrent language. The very existence of a special thread or goroutine forces the programmer to distort the program to avoid crashes and other problems caused by inadvertently operating on the wrong thread.

For those cases where a particular goroutine is truly special, the language provides features such as channels that can be used in flexible ways to interact with it.

Functions and Methods

Why do T and *T have different method sets?

As the Go specification says, the method set of a type T consists of all methods with receiver type T , while that of the corresponding pointer type *T consists of all methods with receiver *T or T . That means the method set of *T includes that of T , but not the reverse.

This distinction arises because 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 safe way for a method call to obtain a pointer. (Doing so would allow a method to modify the contents of the value inside the interface, which is not permitted by the language specification.)

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 an example, if the Write method of bytes.Buffer used a value receiver rather than a pointer, 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.

What happens with closures running as goroutines?

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
    } }
} }

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 .

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:

 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 .

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

 for _, v := range values {
        v := v // create a new 'v'.
        go func() {
            fmt.Println( v )
            done <- true
        }()
    } }

This behavior of the language, not defining a new variable for each iteration, may have been a mistake in retrospect. It may be addressed in a later version but, for compatibility, cannot change in Go version 1.

Control flow

Why does Go not have the ?: operator?

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

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

The reason ?: is absent from Go is that the language's designers had seen the operation used too often to create impenetrably complex expressions. The if-else form, although longer, is unquestionably clearer. A language needs only one conditional control flow construct.

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

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

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

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

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.

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.

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 .

Why isn't X in the standard library?

The standard library's purpose is to support the runtime, connect to the operating system, and provide key functionality that many Go programs require, such as formatted I/O and networking. It also contains elements important for web programming, including cryptography and support for standards like HTTP, JSON, and XML.

There is no clear criterion that defines what is included because for a long time, this was the only Go library. There are criteria that define what gets added today, however.

New additions to the standard library are rare and the bar for inclusion is high. Code included in the standard library bears a large ongoing maintenance cost (often borne by those other than the original author), is subject to the Go 1 compatibility promise (blocking fixes to any flaws in the API), and is subject to the Go release schedule , preventing bug fixes from being available to users quickly.

Most new code should live outside of the standard library and be accessible via the go tool 's go get command. Such code can have its own maintainers, release cycle, and compatibility guarantees. Users can find packages and read their documentation at godoc.org .

Although there are pieces in the standard library that don't really belong, such as log/syslog , we continue to maintain everything in the library because of the Go 1 compatibility promise. But we encourage most new code to live elsewhere.

Implementation 实作

What compiler technology is used to build the compilers?

There are several production compilers for Go, and a number of others in development for various platforms.

The default compiler, gc , is included with the Go distribution as part of the support for the go command. Gc was originally written in C because of the difficulties of bootstrapping—you'd need a Go compiler to set up a Go environment. But things have advanced and since the Go 1.5 release the compiler has been a Go program. The compiler was converted from C to Go using automatic translation tools, as described in this design document and talk . Thus the compiler is now "self-hosting", which means we needed to face the bootstrapping problem. The solution is to have a working Go installation already in place, just as one normally has with a working C installation. The story of how to bring up a new Go environment from source is described here and here .

Gc is written in Go with a recursive descent parser and uses a custom loader, also written in Go but based on the Plan 9 loader, to generate ELF/Mach-O/PE binaries.

At the beginning of the project we considered using LLVM for gc but decided it was too large and slow to meet our performance goals. More important in retrospect, starting with LLVM would have made it harder to introduce some of the ABI and related changes, such as stack management, that Go requires but are not part of the standard C setup. A new LLVM implementation is starting to come together now, however.

The Gccgo compiler is a front end written in C++ with a recursive descent parser coupled to the standard GCC back end.

Go turned out to be a fine language in which to implement a Go compiler, although that was not its original goal. Not being self-hosting from the beginning allowed Go's design to concentrate on its original use case, which was networked servers. Had we decided Go should compile itself early on, we might have ended up with a language targeted more for compiler construction, which is a worthy goal but not the one we had initially.

Although gc does not use them (yet?), a native lexer and parser are available in the go package and there is also a native type checker .

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) but it has since been translated to Go (except for some assembler bits). Gccgo 's run-time support uses glibc . The gccgo compiler implements goroutines using a technique called segmented stacks, supported by recent modifications to the gold linker. Gollvm similarly is built on the corresponding LLVM infrastructure.

Why is my trivial program such a large binary?

The linker in the gc toolchain creates statically-linked binaries by default. All Go binaries therefore include the Go runtime, along with the run-time type information necessary to support dynamic type checks, reflection, and even panic-time stack traces.

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 weighs a couple of megabytes, but that includes more powerful run-time support and type and debugging information.

A Go program compiled with gc can be linked with the -ldflags=-w flag to disable DWARF generation, removing debugging information from the binary but with no other loss of functionality. This can reduce the binary size substantially.

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.

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.

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.
    .... ....
} }

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.

Why does my virus-scanning software think my Go distribution or compiled binary is infected?

This is a common occurrence, especially on Windows machines, and is almost always a false positive. Commercial virus scanning programs are often confused by the structure of Go binaries, which they don't see as often as those compiled from other languages.

If you've just installed the Go distribution and the system reports it is infected, that's certainly a mistake. To be really thorough, you can verify the download by comparing the checksum with those on the downloads page .

In any case, if you believe the report is in error, please report a bug to the supplier of your virus scanner. Maybe in time virus scanners can learn to understand Go programs.

Performance 性能

Why does Go perform badly on benchmark 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 golang.org/x/exp/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.

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.

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.

Changes from C

Why is the syntax so different from 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.

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

 int* a, b;

declares a to be a pointer but not b ; in 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.

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

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.

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.

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 different 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.

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

One of the biggest sources of bookkeeping in systems programs is managing the lifetimes of allocated objects. In languages such as C in which it is done manually, it can consume a significant amount of programmer time and is often the cause of pernicious bugs. Even in languages like C++ or Rust that provide mechanisms to assist, those mechanisms can have a significant effect on the design of the software, often adding programming overhead of its own. We felt it was critical to eliminate such programmer overheads, and advances in garbage collection technology in the last few years gave us confidence that it could be implemented cheaply enough, and with low enough latency, that it could be a viable approach for networked systems.

Much of the difficulty of concurrent programming has its roots in the object lifetime problem: 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.

This is not to say that the recent work in languages like Rust that bring new ideas to the problem of managing resources is misguided; we encourage this work and are excited to see how it evolves. But Go takes a more traditional approach by addressing object lifetimes through garbage collection, and garbage collection alone.

The current implementation is a mark-and-sweep collector. If the machine is a multiprocessor, the collector runs on a separate CPU core in parallel with the main program. Major work on the collector in recent years has reduced pause times often to the sub-millisecond range, even for large heaps, all but eliminating one of the major objections to garbage collection in networked servers. Work continues to refine the algorithm, reduce overhead and latency further, and to explore new approaches. The 2018 ISMM keynote by Rick Hudson of the Go team describes the progress so far and suggests some future approaches.

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.

by  ICOPY.SITE