本章是对于Go语言基本组件的一些说明。希望本章所提供的足够信息和事例,能够使您尽可能快地做一些有用的东西。本书所有的例子都是针对显示世界的任务的。本章将带您尝试体验用Go语言来编写各种程序:从简单的文件、图片处理到并发的客户端和服务器的互联网应用开发。虽然在一章里不能把所有东西讲清楚,但是以这类应用作为学习一门语言的开始是一种高效的方式
学习新语言比较自然的方式,是使用新语言写一些你已经可以用其他语言实现的程序。我们试图说明和解释如何用好Go语言,当你写自己的代码的时候,本章的代码可以作为参考
1.1 hello,world
我们依然从永恒的"hello, world"例子开始,它出现在1978年出版的《The C Program-ming Language》这本书的开头。C对Go的影响非常直接,我们用"hello, world"来说明一些主要的思路
gop1.io/ch1/helloworld
package main
import "fmt"
func main(){
fmt.Println("Hello, 世界")
}
Go是编译型的语言。Go的工具链将程序的源文件转变成机器相关的原生二进制指令。这些工具可以通过单一的go命令配合其子命令进行使用。最简单的子命令是run,它将一个或多个以.go为后缀的源文件进行编译、链接,然后运行生成的可执行文件(本书中我们使用$符号作为命令提示符):
$ go run helloworld.go
不出意料,这将输出:
Hello,世界
Go原生地支持Unicode,所以它可以处理所有国家的语言
如果这个程序不是一次性的实验,那么编译输出成一个可复用的程序比较好。这通过go build来实现:
$ go build helloworld.go
这条命令生成了一个叫作helloworld的二进制程序,它可以不用进行任何其他处理,随时执行:
$ ./helloworld
Hello, 世界
我们给每一个重要的例子都加了一个标签,提示你可以从本书在gop1.io的源码库获取代码:
gop1.io/ch1/helloworld
如果执行go get gop1.io/ch1/helloworld,它将会把源代码取到相应的目录。这将在2.6节和10.7节进行更多的讨论
现在我们来说说该程序本身。Go代码是使用包来组织的,包类似于其他语言中的库和模块。一个包由一个或多个.go源文件组成,放在一个文件夹中,该文件夹的名字描述了包的作用。每一个源文件的开始都用package声明,例子里面是package main,指明了这个文件属于哪个包。后面跟着它导入的其他包的列表,然后是存储在文件中的程序声明
Go的标准库中有100多个包用来完成输入、输出、排序、文本处理等常规任务。例如,fmt包中的函数用格式化输出和扫描输入。Println是fmt中一个基本的输出函数,它输出一个或多个用空格分隔的值,结尾使用一个换行符,这样看起来这些值是单行输出
名为main的包比较特殊,它用来定义一个独立的可执行程序,而不是库。在main包中,函数main也是特殊的,不管在什么程序中,main做什么事情,它总是程序开始执行的地方。当然,main通常调用其他包中的函数来做更多的事情,比如fmt.Println
我们需要告诉编译器源文件需要哪些包,用package声明后面的import来导入这些包。"hello, world"程序仅使用了一个来自于其他包的函数,而大多数函数可能导入更多的包。
你必须精确地导入需要的包。在缺失导入或存在不需要的包的情况下,编译会失败,这种严格的要求可以防止程序演化中引用不需要的包
import声明必须跟在package声明之后。import导入声明后面,是组成程序的函数、变量、常量、类型(以func、var、const、type开头)声明。大部分情况下,声明的顺序是没有关系的。示例中的程序足够短,因为它只声明了一个函数,这个函数又仅仅调用了一个其他的函数。为了节省空间,在处理示例的时候,但是它们存在于源文件中,并且编译时必不可少
一个函数的声明由func关键字、函数名、参数列表(main函数为空)、返回值列表(可以为空)、放在大括号内的函数体组成,函数体定义函数是用来做什么的,这将在第5章详细介绍
Go对于代码的格式化要求非常严格。gofmt工具将代码以标准格式重写,go工具的fmt子命令使用gofmt工具来格式化指定包里的所有文件或者当前文件夹中的文件(默认情况下)。本书中包含的所有Go源代码文件都使用gofmt运行过,你应该养成对自己的代码使用gofmt工具的习惯。定制一个标准的格式,可以省去大量无关紧要的辩论,更重要的是,如果允许随心所欲的格式,各种自动化的源代码转换工具将不可用
许多文本编辑器可以配置为每次在保存文件时自动运行gofmt,因此源文件总可以保持正确的形式。此外,一个相关的工具goimports可以按需管理导入声明的插入和移除。它不是标准发布版的一部分,可以通过执行下面的命令获取到:
$ go get golang.org/x/tools/cmd/goimports
对大多数用户来说,按照常规方式下载、编译包,执行自带的测试,查看文档等操作,使用go工具都可以实现,这将在10.7节详细介绍
1.2 命令行参数
大部分程序处理输入然后产生输出,这就是关于计算的大致定义。但是程序怎样获取数据的输入呢?一些程序自己生成数据,更多的时候,输入来自一个外部源:文件、网络连接、其他程序的输出、键盘、命令行参数等。
os包提供一些函数和变量,以与平台无关的方式和操作系统打交道。命令行参数以os包中Args名字的变量供程序访问,在os包外面,使用os.Args这个名字。
变量os.Args是一个字符串slice。slice是Go中的基础概念,很快我们将讨论到它。现在只需理解它是一个动态容量的顺序数组s,可以通过s[i]来访问单个元素,通过s[m:n]来访问一段连续子区间,数组长度用len(s)表示。与大部分编程语言一样,在Go中,所有的索引使用半开区间,即包含第一个索引,不包含最后一个索引,因为这样逻辑比较简单。例如,slice s[m:n],其中,0<=m<=n<=len(s),包含n-m个元素。
os.Args的第一个元素是os.Args,它是命令本身的名字;;另外的元素是程序开始执行时的参数。表达式s[m:n]表示一个从第m个到第n-1个元素的slice,所以下一个示例中slice需要的元素是os.Args[1:len(os.Args)]。如果m或n缺失,默认分别是0或len(s),所以我们可以将期望的slice简写为os.Args[1:]
这里有一个UNIX echo命令的实现,它将命令行参数输出到一行。该实现导入两个包,使用由圆括号括起来的列表,而不是独立的import声明。两者都是合法的,但为了方便起见,我们使用列表的方式。导入的顺序是没有关系的,gofmt工具会将其按照字母顺序表进行排序(当一个示例有几个版本时,通常给它们编号以区分出当前讨论的版本)