Go 编程入门教程

作者:Jay Ts

如何开始使用这种有用的新编程语言。

您可能听说过 Go 语言。像任何新的编程语言一样,它也经历了一段时间的成熟和稳定,才变得可以用于生产应用。如今,Go 是一种成熟的语言,被用于 Web 开发、编写 DevOps 工具、网络编程和数据库。Docker、Kubernetes、Terraform 和 Ethereum 都是用 Go 编写的。Go 语言的普及速度正在加快,2017 年的采用率增长了 76%,现在已经有了 Go 用户组和 Go 会议。无论您是想增加您的专业技能,还是仅仅对学习一门新的编程语言感兴趣,都应该了解一下它。

Go 语言历史

Google 的三位程序员创建了 Go 语言:Robert Griesemer、Rob Pike 和 Ken Thompson。该团队决定创建 Go 语言,是因为他们对 C++ 和 Java 感到失望,这些语言多年来变得笨重且难以使用。他们希望将乐趣和效率带回编程中。

这三位都成就斐然。Griesemer 曾参与 Google 的超快速 V8 JavaScript 引擎的开发,该引擎用于 Chrome 网页浏览器、Node.js JavaScript 运行时环境以及其他地方。Pike 和 Thompson 是创建 UNIX、C 语言和 UNIX 实用程序的贝尔实验室原始团队的成员,这导致了 GNU 实用程序和 Linux 的开发。Thompson 编写了 UNIX 的第一个版本,并创建了 B 编程语言,C 语言就是基于 B 语言开发的。后来,Thompson 和 Pike 在 Plan 9 操作系统团队工作,他们还共同定义了 UTF-8 字符编码。

为什么选择 Go?

Go 语言兼具静态类型的安全性和垃圾回收,以及编译型语言的速度。对于其他语言,“编译”和“垃圾回收”往往意味着漫长的编译等待时间和运行缓慢的程序。但 Go 语言拥有闪电般快速的编译器,编译时间几乎可以忽略不计,以及现代、超高效的垃圾回收器。您将获得快速的编译时间和快速的程序。Go 语言具有简洁的语法和文法,关键字很少,这使得 Go 语言拥有像 Python、Ruby 和 JavaScript 这样的动态类型解释型语言的简洁性和乐趣。

Go 语言的设计理念是融合多种语言的优点。乍一看,Go 语言很像 C 和 Pascal(它们都是 Algol 60 的后继者)的混合体,但仔细观察,您会发现它也借鉴了许多其他语言的思想。

Go 语言被设计成一种简单易用的编译型语言,同时允许编写简洁且高效运行的程序。Go 语言缺乏冗余的功能,因此可以流畅地编程,而无需在编程时查阅语言文档。Go 语言编程快速、有趣且高效。

开始使用 Go

首先,请确保您已安装 Go 语言。您可能可以使用发行版的软件包管理系统。要查找 Go 软件包,请尝试搜索 “golang”,它是 Go 的同义词。如果您无法通过这种方式安装,或者想要更新的版本,请从 https://golang.ac.cn/dl 获取 tarball,并按照该页面上的说明进行安装。

安装 Go 后,请尝试以下命令


$ go version
go version go1.10 linux/amd64

输出显示我已经在 64 位 Linux 机器上安装了 Go 1.10 版本。

希望到现在您已经产生了兴趣,并想看看一个完整的 Go 程序是什么样的。这是一个非常简单的 Go 程序,它会打印 “hello, world”


package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}

package main 行定义了此文件所属的包。将包和函数命名为 main 会告诉 Go,这是程序执行的起点。即使整个程序中只有一个包和一个函数,您也需要定义一个 main 包和 main 函数。

在顶层,Go 源代码被组织成包。每个源文件都是包的一部分。导入包和导出函数非常简单。

下一行,import "fmt" 导入了 fmt 包。它是 Go 标准库的一部分,包含 Printf() 函数。通常您需要导入多个包。要导入 fmtosstrings 包,您可以键入以下任一代码


import "fmt"
import "os"
import "strings"

或者这样


import (
    "fmt"
    "os"
    "strings"
    )

使用括号,import 应用于括号内列出的所有内容,这节省了一些输入。您会在 Go 语言的其他地方再次看到像这样使用括号,并且 Go 语言还有其他类型的输入快捷方式。

包可以导出常量、类型、变量和函数。要导出某些内容,只需将您要导出的常量、类型、变量或函数的名称大写即可。就这么简单。

请注意,在 “hello, world” 程序中没有分号。行尾的分号是可选的。虽然这很方便,但当您刚开始学习 Go 语言时,需要注意一些事情。Go 语言的这部分语法是使用 BCPL 语言中的一种方法实现的。编译器使用一组简单的规则来“猜测”行尾是否应该有分号,并自动插入一个分号。在这种情况下,如果 main() 中的右括号在行尾,它会触发该规则,因此必须将左大括号放在 main() 后的同一行上。

这种格式在其他语言中是一种常见的做法,但在 Go 语言中,这是必需的。如果您将左大括号放在下一行,您将收到错误消息。

Go 语言的不同寻常之处在于,它要么要求,要么偏爱特定的空白格式样式。该语言并没有允许各种格式样式,而是将单一的格式样式作为其设计的一部分。程序员有很多自由来违反它,但只能在一定程度上。这要么是紧身衣,要么是天赐之物,这取决于您的偏好!许多其他语言允许的自由格式可能会导致一个迷你的巴别塔,使其他程序员难以阅读代码。Go 语言通过使单一的格式样式成为首选来避免这种情况。由于采用标准格式样式并习惯性地使用它相当容易,因此您所要做的就是编写普遍可读的代码。这公平吗?Go 语言甚至附带了一个工具,用于重新格式化您的代码,使其符合标准


$ go fmt hello.go

只有两个注意事项:您的代码必须没有语法错误才能工作,因此它不会修复我刚才描述的那种问题。此外,它会覆盖原始文件,因此如果您想保留原始文件,请在运行 go fmt 之前进行备份。

main() 函数只有一行代码来打印消息。在本例中,使用了 fmt 包中的 Printf() 函数,使其类似于用 C 语言编写 “hello, world” 程序。如果您愿意,您也可以使用以下代码


fmt.Println("hello, world")

以节省输入字符串末尾的 \n 换行符。

现在让我们编译并运行程序。首先,将 “hello, world” 源代码复制到一个名为 hello.go 的文件中。然后使用以下命令编译它


$ go build hello.go

要运行它,请使用生成的名为 hello 的可执行文件作为命令


$ hello
hello, world

作为快捷方式,您可以使用一个命令完成这两个步骤


$ go run hello.go
hello, world

这将编译并运行程序,而无需创建可执行文件。当您正在积极开发项目并且只是在进行更多编辑之前检查错误时,这非常有用。

接下来,让我们看一下 Go 语言的一些主要特性。

并发

Go 语言内置的对并发的支持,以goroutine 的形式,是该语言的最佳特性之一。goroutine 类似于进程或线程,但它要轻量得多。一个 Go 程序拥有数千个活动的 goroutine 是很正常的。启动一个 goroutine 非常简单,只需


go f()

然后函数 f() 将与主程序和其他 goroutine 并发运行。Go 语言有一种允许程序的并发部分使用通道进行同步和通信的方法。通道有点像 UNIX 管道;它可以在一端写入,在另一端读取。通道的常见用途是 goroutine 指示它们何时完成。

goroutine 及其资源由 Go 运行时系统自动管理。借助 Go 语言的并发支持,很容易让多核 CPU 的所有核心和线程高效工作。

类型、方法和接口

您可能想知道为什么类型和方法在同一个标题下。这是因为 Go 语言有一个简化的面向对象编程模型,它与表达性强、轻量级的类型系统协同工作。它完全避免了类和类型层次结构,因此可以在不造成混乱的情况下使用数据类型完成复杂的事情。在 Go 语言中,方法附加到用户定义的类型,而不是类、对象或其他数据结构。这是一个简单的例子


// make a new type MyInt that is an integer

type MyInt int

// attach a method to MyInt to square a number

func (n MyInt) sqr() MyInt {
    return n*n
}

// make a new MyInt-type variable
// called "number" and set it to 5

var number MyInt = 5

// and now the sqr() method can be used

var square = number.sqr()

// the value of square is now 25

与此同时,Go 语言有一个名为接口的工具,允许混合类型。只要每种类型都附加了接口定义中指定的、操作所需的 method 或 methods,就可以对混合类型执行操作。

假设您创建了名为 catdogbird 的类型,并且每种类型都有一个名为 age() 的方法,该方法返回动物的年龄。如果您想在一个操作中将所有动物的年龄相加,您可以定义如下接口


type animal interface {
    age() int
}

然后 animal 接口可以像类型一样使用,允许在计算年龄时将 catdogbird 类型一起处理。

Unicode 支持

考虑到 Ken Thompson 和 Rob Pike 定义了现在全球占主导地位的 Unicode UTF-8 编码,Go 语言对 UTF-8 有良好的支持可能不会让您感到惊讶。如果您从未使用过 Unicode 并且不想为此烦恼,请不要担心;UTF-8 是 ASCII 的超集。这意味着您可以继续使用 ASCII 进行编程,并忽略 Go 语言的 Unicode 支持,一切都会运行良好。

实际上,所有源代码都被 Go 编译器和工具视为 UTF-8。如果您的系统配置正确,允许您输入和显示 UTF-8 字符,您可以在 Go 源代码文件名、命令行参数以及 Go 源代码中用于文字字符串以及变量、函数、类型和常量的名称。

在图 1 中,您可以看到一个葡萄牙语的 “hello, world” 程序,就像巴西程序员可能编写的那样。

""

图 1. 葡萄牙语的 Go “Hello, World” 程序

除了在这些方面支持 Unicode 之外,Go 语言的标准库中还有三个包用于处理涉及 Unicode 的更复杂的问题。

到现在,您可能明白为什么 Go 程序员对这门语言如此热情了。不仅仅是因为 Go 语言有这么多优点,而且所有这些优点都包含在一种旨在避免过度复杂化的语言中。这是一个整体大于部分之和的真正好例子。

资源

Jay Ts 是一位软件开发人员、Linux 系统管理员和电子设计师。他于 1981 年开始接触 UNIX 和 C 编程语言,并于 1996 年从 UNIX 转向 Linux。他熟悉许多操作系统、Linux 发行版和编程语言。Jay 曾为加州理工学院、NASA/JPL 和信息科学研究所工作。他目前居住在亚利桑那州塞多纳。请将评论发送至 jay@jayts.com。

加载 Disqus 评论