[译] Go 语言概览

Go 语言概览

本文摘要:本文很是笼统地总结了 Go 语言的定义、生态系统和实现方式,也尽力给出了与不一样的需求所对应的参考文档,详情参见本文末尾。html

每当咱们提及“Go 语言”的时候,可能会由于场景的不一样聊到不少彻底不一样的东西。所以,我尝试着对 Go 语言和其生态系统作一个概述,并在各部份内容中都列出相关的文档(这可能有点像是大杂烩,其中还包含了我最近实际遇到的许多问题)。让咱们开始吧:前端

Go 编程语言

Go 语言是一种编程语言。做为一种权威,Go 语言规范中定义了代码的格式规范和代码所表明的含义。不符合该规范的都不是 Go 语言。一样地,该规范中没有提到的内容不视为该语言的一部分。目前由 Go 语言开发团队维护该规范,每半年发布一个新版本。在我写这篇文章的时候最新的版本是 1.12android

Go 语言规范规定了:ios

  • 语法
  • 变量的类型、值,及其语义
  • 预先声明的标识符及其含义
  • Go 程序的运行方式
  • 特殊的 unsafe 包(虽然没有包含全部的语义)

该规范应该已经足够让你实现一个 Go 语言的编译器了。实际上,已经有不少人基于此实现了许多不一样的编译器。git

Go 编译器及其运行时

该语言规范只是一份文本文档,它自己不太有用。你须要的是实现了这些语义的软件,即编译器(分析、检查源代码,并将其转换为可执行的形式)和运行时(提供运行代码时所需的环境)。有不少这样的软件组合,他们都或多或少有些不一样。示例以下:程序员

  • gc,Go 语言开发团队本身开发的纯 Go 语言实现的(有一小部分汇编实现)编译器和运行时。它随着 Go 语言一块儿发布。与其余此类工具不一样的是,gc 并不严格区分编译器、组装器和连接器 —— 它们在实现的时候共享了大量的代码,而且会共享或传递一些重要职责。所以,一般没法连接由不一样版本的 gc 所编译的包。
  • gccgo 和 libgo,gcc 的前端和其运行时。它是用 C 实现的,而且也由 Go 开发团队维护。然而,因为它是 gcc 组织的一部分,并根据 gcc 的发布周期发布,所以一般会稍微落后于 Go 语言规范的“最新”版本。
  • llgo,LLVM 的前端。我对其不太了解。
  • gopherjs,将 Go 代码编译为 JavaScript,并使用一个 JavaScript VM 和一些自定义代码做为运行时。长远来看,因为 gc 得到了 WebAssembly 的原生支持,它有可能会被淘汰。
  • tinygo,针对小规模编程的不完整实现。它能够经过自定义一个运行时运行在微控制器(裸机)或者 WebAssembly 虚拟机上。因为它的局限性,技术上来讲它并无实现 Go 语言的全部特性 —— 主要体如今它缺乏垃圾回收器、并发和反射。

还有更多其余的实现,但这已经足以让你了解不一样的实现方式。以上每一种方法都使用了不一样的方式来实现 Go 语言,并具备本身不同凡响的特性。他们可能存在的不一样之处有(为了说明这一点,下面的某些说法可能会有点奇特):github

  • int/uint 的大小 —— 长度可能为 32 位或 64 位。
  • 运行时中基础功能的实现方式,如内存分配、垃圾回收和并发的实现。
  • 遍历 map 的顺序并无在 Go 语言中定义 —— gc 显然会将这类操做随机化,而 gopherjs 会用你使用的 JavaScript 实现遍历。
  • append 操做分配的所需额外内存空间大小 —— 可是,在分配额外空间时不会再次分配更多的内存空间。
  • unsafe.Pointeruintptr 之间的转换方式。特别指出,gc 对于该转换什么时候应该生效有本身的规则。一般状况下,unsafe 包是虚拟的,它会在编译器中被实现。

通常来讲,根据规范中没有提到的某些细节(尤为是上面提到的那些细节)可使你的程序用不一样的编译器也能编译,但每每程序不会像你预期的那样正常工做。所以,你应该尽力避免此类事情发生。golang

若是你的 Go 语言是经过“正常”渠道安装的话(在官网上下载安装,或是经过软件包管理器安装),那么你会获得 Go 开发团队提供的 gc 和正式的运行时。在本文中,当咱们在讨论“Go 是如何作的”时,若没有在上下文特别指明,咱们一般就是在谈论 gc。由于它是最重要的一个实现。编程

标准库

标准库是 Go 语言中附带的一组依赖包,它能够被用来当即构建许多实用的应用程序。它也由 Go 开发团队维护,而且会随着 Go 语言和编译器一块儿发布。通常来讲,标准库的某种实现只能依赖与其共同发布的编译器才能正常使用。由于大部分(但不是全部)运行时都是标准库的一部分(主要包含在 runtimereflectsyscall 包中)。因为编译器在编译时须要兼容当前使用的运行时,所以它们的版本要相同。标准库的 API 是稳定的,不会以不兼容的方式改变,因此基于某个指定版本的标准库编写的 Go 程序在编译器的将来版本中也能够正常运行。c#

有些标准库会彻底本身实现整个库中的全部内容,而有些则只实现一部分 —— 开发者尤为会在 runtimereflectunsafesyscall 包中实现自定义的功能。举个例子,我相信 AppEngine 标准库是出于安全考虑从新实现了标准库的部分功能的。这类从新实现的部分一般会尽可能对用户保持透明。

还存在一种标准库之外的独立库,通俗地说这就是 x 或者说是“扩展库”。这种库包含了 Go 开发团队同时开发和维护的部分代码,可是不会与 Go 语言有相同的发布周期,而且相比于 Go 语言自己,兼容性也会较差(功能性和维护性也会较差)。其中的代码要么是实验性的(在将来可能会包含在标准库中),要么是比起标准库中的功能还不够泛用,或者是在某些罕见的状况下,提供一种开发者们能够与 Go 开发团队同步进行代码审查的方式。

再一次强调,若是没有额外地指出,在提到“标准库”时,咱们指的是官方维护和发布的、托管在 golang.org 上的 Go 标准库。

代码构建工具

咱们须要代码构建工具来使 Go 语言易于使用。构建工具的主要职责是找到须要编译的包和全部的依赖项,并依据必要的参数调用编译器和连接器。Go 语言有对包的支持,容许在编译时把多个源代码文件视为一个单元。这也定义了导入和使用其余包的方式。但重要的是,这并无定义导入包的路径与源文件的映射方式,也没有定义导入包在磁盘中的位置。所以,每种构建工具对于该问题都有不一样的处理方式。你可使用通用构建工具(如 Make 命令),但也有许多专门为 Go 语言而生的构建工具:

  • Go 语言工具[1]是 Go 开发团队官方维护的构建工具。它与 Go 语言(gc 和标准库)有相同的发布周期。它须要一个名为 GOROOT 的目录(该值从环境变量中获取,会在安装时产生一个默认值)来存放编译器、标准库和其余各类工具。它要求全部的源代码都要存放在一个名为 GOPATH 的目录下(该值也从环境变量中获取,默认为 $HOME/go 或是一个与其相等的值)。举例来讲,包 a/b 的源代码应该位于诸如 $GOPATH/src/a/b/c.go 的路径下。而且 $GOPATH/src/a/b 路径下应该包含一个包下的源文件。在分布式的模式下,有一种机制能够从任意服务器上递归地下载某个包及其依赖项,即便这种机制不支持版本控制或是下载校验。Go 语言工具中也包含了许多其余工具包,包括用于测试 Go 代码的工具、阅读文档的工具(golang.org 是用 Go 语言工具部署的)、提交 bug 的工具和其余各类小工具。
  • gopherjs 自带的构建工具,它在很大程度上模仿了 Go 语言工具。
  • gomobile 是一个专门为移动操做系统构建 Go 代码的工具。
  • depgbglide 等等是社区开发的构建和依赖项管理工具,它们各自都有本身独特的文件布局方式(有些能够与 Go 语言工具兼容,有些则不兼容)和依赖项声明方式。
  • bazel 是谷歌内部构建工具的开源版本。虽然它的使用实际上并不限于 Go 语言,但我之因此把它列为单独的一项,是由于人们常说 Go 语言工具旨在为谷歌服务,而与社区的需求相冲突。然而,Go 语言工具(和其余许多开放的工具)是没法被谷歌所使用的,缘由是 bazel 使用了不兼容的文件布局方式。

代码构建工具是大多数用户在编写代码时直接使用的重要工具,所以它很大程度上决定了 Go 语言生态系统的方方面面,也决定了包的组合方式,这也将影响 Go 程序员之间的沟通和交流方式。如上所述,Go 语言工具是被隐式引用的(除非指定了其余的运行环境),所以它的设计会让公众对 “Go 语言”的见解形成很大的影响。虽然有许多替代工具可供使用,这些工具也已经在如公司内部使用等场景被普遍使用,可是开源社区一般但愿 Go 语言工具与 Go 语言的使用方式相契合,这意味着:

  • 能够获取源代码。Go 语言工具对包的二进制分发只作了极其有限的支持,而且仅有的支持将会在未来的版本中移除。
  • 要依据 Go 官方文档编排格式来撰写文档。
  • 包含测试用例,而且能经过 go test 运行测试。
  • 能够彻底经过 go build 来编译(与后面所述的特征共同被称为“能够经过 Go 获得的” —— “go-gettable”)。特别指出,若是须要生成源代码或是元编程,则使用 go generate 并提交生成的构件。
  • 经过命名空间导入的路径其第一部分是一个域名,该域名能够是一个代码托管服务器或者是该服务器上运行的一个 Web 服务,则 Go 代码能够找到源代码和其依赖,而且能够正常工做
  • 每一个目录都只有一个包,而且可使用代码构建约束条件进行条件编译。

Go 语言工具的文档很是全面,它是一个学习 Go 如何实现各类生态系统的良好起点。

其余工具

Go 语言的标准库包含了一些能够与 Go 源代码交互的包包含了更多功能的 x/tools 扩展库。Go 语言也所以在社区中有很是强的第三方工具开发文化(因为官方强烈地想要保持 Go 语言自己的精简)。这些工具一般须要知道源代码的位置,可能还须要获取类型信息。go/build 包遵循了 Go 语言工具的约定,所以它自己就能够做为其部分构建过程的文档。缺点则是,构建在它之上的工具备时与基于其余构建工具的代码不兼容。所以有一个新的包正在开发中,它能够与其余构建工具很好地集成。

实际上 Go 语言的工具备很是多,而且每一个人都有本身的偏好。但大体以下:

总结

我想用一个简短的参考文献列表来结束这篇文章,列表的内容是为那些感到迷茫的初学者准备的。请点击下面的连接:

除此之外还有许多有价值的文档能够做为补充,但这些应该已经足够让你有一个良好的开端了。做为一个 Go 语言的初学者,若是你发现本文有任何遗漏之处(我可能会补充更多的细节)或者你找到了任何有价值的参考资料,请经过 Twitter 联系我。若是你已是一个经验丰富的 Go 语言开发者,而且你发现我遗漏了某些重要的内容(可是我有意忽略了一些重要的参考资料,使得初学者们能够感觉到 Go 语言学习中的新鲜感:smile:),也请给我留言。


[1] 注:Go 开发团队目前正在对模块作一些支持,模块是包之上的代码分发单元,这些支持包括版本控制和一些可使“传统” Go 语言工具解决问题的基础工做。等这些支持完成之后,这一段中的全部内容基本上就都过期了。对模块的支持目前是有的,但还不是 Go 语言的一部分。因为本文的核心内容是对 Go 语言的不一样组成部分进行简要介绍,这些内容是不太容易发生变化的,目前来看我认为理解这些历史问题也是颇有必要的。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索